Tholp's bespoke website generator
at main 340 lines 9.3 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 // 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}