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