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"); }
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"); }
130 if let Some(x) = list_types.last() {
131 if x == &"ol" {
132 out += &("</".to_string() + list_types.pop().unwrap() + ">\n"); 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"); }
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"); }
171 if let Some(x) = list_types.last() {
172 if x == &"ul" {
173 out += &("</".to_string() + list_types.pop().unwrap() + ">\n"); 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 }}
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}