Tholp's bespoke website generator
1use super::DELIMITERS;
2use crate::types::Token;
3
4//TODO: Theres a couple functions that are still written like tokens are strings not chars, they work fine
5// for now but they may need to be changed later
6
7pub fn collect_arguments(tokens: &[Token]) -> Option<(Vec<String>, usize)> {
8 // Returns arguments vec and number of tokens to be consumed
9 //let mut output = Vec::new();
10
11 let mut quoted: bool = false;
12 let mut escaped: bool = false;
13 let mut entered: bool = false;
14 let mut arg = "".to_string();
15 let mut args: Vec<String> = Vec::new();
16
17 let mut in_token_count = 0;
18 let mut exited_cleanly = false;
19
20 for tok in tokens {
21 let c = tok.contents;
22
23 in_token_count += 1;
24 if c.is_whitespace() && !entered {
25 continue;
26 }
27
28 if !entered && c == '(' {
29 entered = true;
30 continue;
31 }
32
33 if !entered {
34 break;
35 }
36
37 if !quoted && tok.contents == ')' {
38 exited_cleanly = true;
39 if !arg.is_empty() {
40 args.push(arg.clone());
41 arg.clear();
42 }
43 break;
44 }
45
46 if c == '\"' && !escaped {
47 quoted = !quoted;
48 // either fucked or empty string
49 if !quoted && arg.len() == 0 {
50 args.push("".into());
51 }
52
53 continue;
54 }
55
56 if c == '\\' && !escaped {
57 escaped = true;
58 continue;
59 }
60
61 if c.is_whitespace() && !quoted {
62 if !arg.is_empty() {
63 args.push(arg.clone());
64 arg.clear();
65 }
66 continue;
67 }
68 arg.push(c);
69 }
70
71 if !entered || !exited_cleanly {
72 return None;
73 }
74 return Some((args, in_token_count));
75}
76
77pub fn collect_block(tokens: &[Token]) -> Option<(Vec<Token>, usize)> {
78 let mut entered = false;
79 let mut tokens_consumed: usize = 0;
80 let mut entering_bracket_count = 0;
81 let mut exiting_bracket_count = 0;
82 let mut scope_count = 0; //incremented by '{{{', decremented by '}}}'
83 let mut escaped = false;
84
85 let mut block: Vec<Token> = Vec::new();
86
87 // We dont really care about doing anything that in the block right now
88 // maybe have the Token struct contain scope level later?
89 let mut escaped_tok: Token = Token::new('\\', 0, 0);
90 for tok in tokens {
91 tokens_consumed += 1;
92 if !entered {
93 if tok.contents.is_whitespace() {
94 continue;
95 }
96 if tok.contents != '{'
97 // Expected block start, got garbage
98 {
99 // println!("Expected block start, got {}",tok.contents);
100 // for t in &block
101 // {
102 // print!("{} ", t.contents);
103 // }
104 // exit(1);
105 return None;
106 }
107 }
108
109 let mut escaped_used = false;
110
111 // Scope Start
112 if tok.contents == '{' && !escaped {
113 entering_bracket_count += 1;
114
115 if entering_bracket_count == 3 {
116 scope_count += 1;
117 entering_bracket_count = 0;
118 if !entered {
119 entered = true;
120 }
121 }
122 } else {
123 entering_bracket_count = 0;
124 if escaped {
125 escaped_used = true;
126 }
127 }
128 // Scope End
129 if tok.contents == '}' && !escaped {
130 exiting_bracket_count += 1;
131 if exiting_bracket_count == 3 {
132 scope_count -= 1;
133 entering_bracket_count = 0;
134 }
135 if scope_count == 0 {
136 break;
137 }
138 } else {
139 exiting_bracket_count = 0;
140 if escaped {
141 escaped_used = true;
142 }
143 }
144
145 if escaped_used {
146 escaped = false;
147 block.push(escaped_tok.clone());
148 }
149
150 if tok.contents == '\\' {
151 escaped = true;
152 escaped_tok = tok.clone();
153 } else {
154 block.push(tok.clone());
155 }
156 }
157
158 if scope_count != 0 {
159 return None;
160 }
161
162 // if block.len() == 6
163 // // things get ugly if its empty
164 // {
165 // let mut emptyblock = Vec::new();
166 // emptyblock.push(Token::new(
167 // "".into(),
168 // tokens[0].origin_file,
169 // tokens[0].line_number,
170 // ));
171 // return (emptyblock, tokens_consumed);
172 // }
173 // pop brackets, bad and ugly but idgaf
174 block.drain(..3);
175 block.drain(block.len() - 2..);
176 return Some((block, tokens_consumed));
177}
178
179// Theres no std function to have the delimiters be their own element in the out vector
180// clean it up a bit here
181pub fn split_keep_delimiters(instr: String) -> Vec<String> {
182 let split: Vec<&str> = instr.split_inclusive(DELIMITERS).collect();
183 let mut output = Vec::new();
184
185 for s in split {
186 if s.ends_with(DELIMITERS) {
187 let (token, ending) = s.split_at(s.len() - 1);
188 if token.len() > 0 {
189 output.push(token.to_string());
190 }
191 output.push(ending.to_string());
192 //println!("({}, {})", token.to_string(), ending.to_string())
193 } else {
194 output.push(s.to_string());
195 }
196 }
197 return output;
198}
199
200pub fn strings_to_tokens(in_strings: Vec<String>, origin_file: usize) -> Vec<Token> {
201 let mut tokens = Vec::new();
202 let mut line_count = 1;
203
204 for str in in_strings {
205 for c in str.chars() {
206 let current_line = line_count;
207 for char in str.chars() {
208 if char == '\n' {
209 line_count += 1;
210 }
211 }
212 let token: Token = Token::new(c, origin_file, current_line);
213 tokens.push(token);
214 }
215 }
216
217 return tokens;
218}
219
220// Need to do some special case stuff so you can macros without spaces between
221// (something like "stuff!insert(..)" is split to ["stuff","!insert(..)"] so it can be acted on later)
222pub fn split_to_tokens(instr: String, origin_file: usize) -> Vec<Token> {
223 let split = split_keep_delimiters(instr);
224 let mut new_split: Vec<String> = Vec::new();
225 for s in split {
226 let prefix_offset = s.find(&['!', '&']);
227 if prefix_offset.is_some() {
228 let (first, second) = s.split_at(prefix_offset.unwrap());
229 //println!("\"{}\", \"{}\"", first, second);
230 if first.len() > 0 {
231 new_split.push(first.to_string());
232 }
233 if second.len() > 0 {
234 new_split.push(second.to_string());
235 }
236 } else {
237 if s.len() > 0 {
238 new_split.push(s);
239 }
240 }
241 //sleep(std::time::Duration::from_millis(10));
242 }
243 return strings_to_tokens(new_split, origin_file);
244}
245
246pub fn next_nonwhitespace_token(tokens: &Vec<Token>, index: usize) -> Option<usize> {
247 while index < tokens.len() {
248 if tokens[index].contents.is_whitespace() {
249 continue;
250 }
251 return Some(index);
252 }
253 return None;
254}
255
256//trim whitespace from the ends
257pub fn trim_whitespace_tokens(tokens: &[Token]) -> &[Token] {
258 if tokens.len() == 0 {
259 return tokens;
260 }
261
262 let mut start: usize = 0;
263 let mut end: usize = tokens.len();
264 for tok in tokens {
265 if !tok.contents.is_whitespace() {
266 break;
267 }
268 start = start + 1;
269 }
270
271 for tok in tokens.iter().rev() {
272 if !tok.contents.is_whitespace() {
273 break;
274 }
275 end = end - 1;
276 }
277
278 return &tokens[start..end];
279}
280
281// Find the first instance of the pattern
282pub fn find_pattern(tokens: &[Token], pat: String) -> Option<(usize, usize)> {
283 // (startpoint, length)
284
285 let split_pattern = split_to_tokens(pat, 0);
286 let mut pattern_index: usize = 0;
287 let mut token_index: usize = 0;
288
289 while token_index < tokens.len() && tokens.len() - token_index >= split_pattern.len() {
290 for t in &tokens[token_index..] {
291 if t.contents == split_pattern[pattern_index].contents {
292 pattern_index += 1;
293 if pattern_index == split_pattern.len() {
294 return Some((token_index, split_pattern.len()));
295 }
296 } else {
297 pattern_index = 0;
298 token_index += 1;
299 break;
300 }
301 }
302 }
303
304 None
305}
306
307pub trait WhitespaceChecks {
308 fn is_only_whitespace(&self) -> bool;
309 fn contains_whitespace(&self) -> bool;
310}
311
312impl WhitespaceChecks for String {
313 fn is_only_whitespace(&self) -> bool {
314 for c in self.chars() {
315 if !c.is_whitespace() {
316 return false;
317 }
318 }
319 return true;
320 }
321
322 fn contains_whitespace(&self) -> bool {
323 for c in self.chars() {
324 if c.is_whitespace() {
325 return true;
326 }
327 }
328 return false;
329 }
330}
331
332pub trait TokenTools {
333 fn trim_whitespace(&mut self) -> &[Token];
334}
335
336impl TokenTools for Vec<Token> {
337 fn trim_whitespace(&mut self) -> &[Token] {
338 return trim_whitespace_tokens(&self[..]);
339 }
340}