Tholp's bespoke website generator
at main 9.1 kB view raw
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}