kami_parser/
syntax.rs

1use crate::lexer::tokenize;
2use crate::multiline_lexer::block_lexer;
3use crate::lexer::Token;
4use crate::lexer::TokenType;
5use crate::multiline_lexer::get_list_depth;
6use htmlentity::entity::*;
7
8pub(crate) fn parse_attr(inp: &str) -> String {
9	if inp == "{}" || inp == "" { 
10		return String::new()
11	}
12	let input = &inp[1..&inp.len()-1];
13	let mut id = String::new();
14	let mut class = String::new();
15	let mut current_type = "none";
16	let mut everything_else = String::new();
17	let mut out = String::new();
18	for (_, cha) in input.char_indices() {
19		match current_type {
20			"none" => {
21				match cha {
22					'.' => current_type = "class",
23					'#' => {
24						id = String::new();
25						current_type = "id";
26					},
27					_ => everything_else += &cha.to_string(),
28				}
29			},
30			"class" => {
31				match cha {
32					' ' => {
33						class += &cha.to_string();
34						current_type = "none";
35					},
36					_ => class += &cha.to_string(),
37				}
38			},
39			"id" => {
40				match cha {
41					' ' => {
42						current_type = "none";
43					},
44					_ => id += &cha.to_string(),
45				}
46			},
47			_ => panic!("Attribute parser reached undefined state"),
48		}
49	}
50	if id != "" {
51		out += &("id=\"".to_string() + &id + "\" ");
52	}
53	if class != "" {
54		out += &("class=\"".to_string() + &class + "\" ");
55	}
56	out + &everything_else
57}
58
59pub fn parse(input: &str) -> (String, String) {
60	let mut tokvec:Vec<Vec<Token>> = Vec::new();
61	let mut out = String::new();
62	let mut warnings = String::new();
63	for i in input.lines() {
64		let (tokens, warns) = tokenize(i);
65		tokvec.push(tokens);
66		warnings += &warns;	
67	}
68	let blocks = block_lexer(&tokvec);
69	for block in blocks {
70		match block.class {
71			TokenType::Para => out += &("<p ".to_owned() + &parse_attr(&block.attributes) + ">" + &parse_line(&block.subtokens) + "</p>\n"),
72			TokenType::Image => out += &("<img ".to_owned() + &parse_attr(&block.attributes) + " src=\"" + &block.content[1..block.content.len()-1] + "\"/>\n"),
73			TokenType::Header => out += &("<h".to_owned() + &block.content.len().to_string() + " " + &parse_attr(&block.attributes) + ">" + &parse_line(&block.subtokens) + "</h" + &block.content.len().to_string() + ">\n"),
74			TokenType::Html => {
75				if block.content == "<>" {
76					out += &(parse_line(&block.subtokens) + "\n");
77				} else {
78					out += &(block.content.to_owned() + &parse_line(&block.subtokens) + "\n");
79				}
80			},
81			TokenType::Table => {
82				out += &("<table ".to_owned() + &parse_attr(&block.attributes) + ">\n");
83				for row in block.subtokens {
84					out += &("<tr ".to_owned() + &parse_attr(&row.attributes) + ">\n");
85					for cell in row.subtokens {
86						let htag = match cell.class {
87							TokenType::TableCell => "td",
88							TokenType::TableHeader => "th",
89							_ => panic!("Non-table token when expecting table token"),
90						};
91						out += &("<".to_owned() + htag + " " + &parse_attr(&cell.attributes)+ ">");
92						out += &parse_line(&cell.subtokens);
93						out += &("</".to_owned() + htag + ">\n");
94					}
95					out += "</tr>\n";
96				}
97				out += "</table>\n";
98			},
99			TokenType::ListBlock => {
100				let mut list_types: Vec<&str> = Vec::new();
101				let mut last_level = 0;
102				for i in block.subtokens.iter() {
103					match i.class {
104						TokenType::UList => {
105							match list_types.last(){
106								None => {
107									out += &("<ul ".to_owned() + &parse_attr(&i.attributes) + ">\n");
108									last_level = 1;
109									list_types.push("ul");
110									out += &parse_line(&i.subtokens);
111								},
112								Some(x) => {
113									let last_depth = get_list_depth(&i);
114									if x == &"ul" {
115										if last_depth > last_level {
116											out += &("<ul ".to_owned() + &parse_attr(&i.attributes) + ">\n");
117											list_types.push("ul");
118										} else if last_depth < last_level {
119											for _ in last_depth..last_level {
120												out += &("</".to_string() + list_types.pop().unwrap() + ">\n"); // This should close the latest opened list type
121											}
122										}
123										last_level = last_depth;
124										out += &parse_line(&i.subtokens);
125									} else {
126										if last_depth < last_level {
127											for _ in last_depth..last_level {
128												out += &("</".to_string() + list_types.pop().unwrap() + ">\n"); // This should close the latest opened list type
129											}
130											if let Some(x) = list_types.last() {
131												if x == &"ol" {
132													out += &("</".to_string() + list_types.pop().unwrap() + ">\n"); // This should close the latest opened list type
133													last_level -= 1;
134												}
135											}
136										}
137										out += &("<ul ".to_owned() + &parse_attr(&i.attributes) + ">\n");
138										last_level += 1;
139										list_types.push("ul");
140										out += &parse_line(&i.subtokens);
141									}
142								}
143							}
144						},
145						TokenType::OList => {
146							match list_types.last(){
147								None => {
148									out += &("<ol ".to_owned() + &parse_attr(&i.attributes) + ">\n");
149									last_level = 1;
150									list_types.push("ol");
151									out += &parse_line(&i.subtokens);
152								},
153								Some(x) => {
154									let last_depth = get_list_depth(&i);
155									if x == &"ol" {
156										if last_depth > last_level {
157											out += &("<ol ".to_owned() + &parse_attr(&i.attributes) + ">\n");
158											list_types.push("ol");
159										} else if last_depth < last_level {
160											for _ in last_depth..last_level {
161												out += &("</".to_string() + list_types.pop().unwrap() + ">\n"); // This should close the latest opened list type
162											}
163										}
164										last_level = last_depth;
165										out += &parse_line(&i.subtokens);
166									} else {
167										if last_depth <= last_level {
168											for _ in last_depth..last_level {
169												out += &("</".to_string() + list_types.pop().unwrap() + ">\n"); // This should close the latest opened list type
170											}
171											if let Some(x) = list_types.last() {
172												if x == &"ul" {
173													out += &("</".to_string() + list_types.pop().unwrap() + ">\n"); // This should close the latest opened list type
174													last_level -= 1;
175												}
176											}
177										}
178										out += &("<ol ".to_owned() + &parse_attr(&i.attributes) + ">\n");
179										last_level += 1;
180										list_types.push("ol");
181										out += &parse_line(&i.subtokens);
182									}
183								}
184							}
185						}
186						_ => ()
187					}
188				}
189				for _ in 0..last_level {
190					match list_types.pop() {
191						None => (),
192						Some(x) => out += &("</".to_string() + x + ">\n"),
193					}// This should close the latest opened list type
194				}
195			}
196			_ => (),
197		}
198	}
199	(out, warnings)
200}
201
202
203fn parse_line(input: &Vec<Token>) -> String {
204	let mut out = String::new();
205	let mut iter: usize = 0;
206	for i in input.iter() {
207		match i.class {
208			TokenType::Put => out += &encode(&i.content, EntitySet::SpecialCharsAndNoASCII, EncodeType::NamedOrHex).iter().collect::<String>(),
209			TokenType::Bold => out += &("<b ".to_owned() + &parse_attr(&i.attributes) + ">" + &parse_line(&i.subtokens) + "</b>"),
210			TokenType::Italic => out += &("<i ".to_owned() + &parse_attr(&i.attributes) + ">" + &parse_line(&i.subtokens) + "</i>"),
211			TokenType::Emphasis => out += &("<em ".to_owned() + &parse_attr(&i.attributes) + ">" + &parse_line(&i.subtokens) + "</em>"),
212			TokenType::Strong => out += &("<strong ".to_owned() + &parse_attr(&i.attributes) + ">" + &parse_line(&i.subtokens) + "</strong>"),
213			TokenType::Sub => out += &("<sub ".to_owned() + &parse_attr(&i.attributes) + ">" + &parse_line(&i.subtokens) + "</sub>"),
214			TokenType::Sup => out += &("<sup ".to_owned() + &parse_attr(&i.attributes) + ">" + &parse_line(&i.subtokens) + "</sup>"),
215			TokenType::Span => out += &("<span ".to_owned() + &parse_attr(&i.attributes) + ">" + &parse_line(&i.subtokens) + "</span>"),
216			TokenType::Strike => out += &("<del ".to_owned() + &parse_attr(&i.attributes) + ">" + &parse_line(&i.subtokens) + "</del>"),
217			TokenType::Under => out += &("<u ".to_owned() + &parse_attr(&i.attributes) + ">" + &parse_line(&i.subtokens) + "</u>"),
218			TokenType::Html => out += &i.content,
219			TokenType::Code => out += &("<code ".to_owned() + &parse_attr(&i.attributes) + ">" + &encode(&i.content[1..i.content.len()-1], EntitySet::SpecialCharsAndNoASCII, EncodeType::NamedOrHex).iter().collect::<String>() + "</code>"),
220			TokenType::LineBreak => out += "</br>",
221			TokenType::Image => out += &("<img ".to_owned() + &parse_attr(&i.attributes) + " src=\""+ &i.content[1..i.content.len()-1] + "\"/>"),
222			TokenType::LinkName => {
223				let parsed_name = parse_line(&i.subtokens);
224				if iter < input.len() - 1 {
225					let next = &input[iter + 1];
226					match next.class {
227						TokenType::LinkDir => out += &("<a href=\"".to_owned() + &next.content[1..next.content.len()-1] + "\" " + &parse_attr(&next.attributes) + ">" + &parsed_name + "</a>"),
228						_ => out += &("<a href=\"".to_owned() + &parsed_name + "\" "  + &parse_attr(&i.attributes) + ">" + &parsed_name + "</a>"),
229					}
230				} else { out += &("<a href=\"".to_owned() + &parsed_name + "\" "  + &parse_attr(&i.attributes) + ">" + &parsed_name + "</a>"); }
231			},
232			TokenType::ListEl | TokenType::NumberedListEl => out += &("<li ".to_owned() + &parse_attr(&i.attributes) + ">" + &parse_line(&i.subtokens) + "</li>\n"),
233			TokenType::LinkDir => (),
234			_ => out += &i.content,
235		}
236		iter += 1;
237	}
238	out
239}