Something went wrong. Try again.
Tholp's bespoke website generator
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477use mlua::ObjectLike;
use super::DELIMITERS;use crate::{ console::error_skid, process_skid, project::Project, types::{SkidContext, Token},};
//TODO: Theres a couple functions that are still written like tokens are strings not chars, they work fine// for now but they may need to be changed later
pub fn collect_arguments( tokens: &[Token], proj_context: &mut Project, skid_context: &mut SkidContext,) -> Option<(Vec<String>, usize)> { // Returns arguments vec and number of tokens to be consumed //let mut output = Vec::new();
let mut quoted: bool = false; let mut escaped: bool = false; let mut entered: bool = false; let mut arg = "".to_string(); let mut args: Vec<String> = Vec::new();
let mut consumed = 0; let mut exited_cleanly = false;
while consumed < tokens.len() { let c = tokens[consumed].contents;
consumed += 1; if c.is_whitespace() && !entered { continue; }
if !entered && c == '(' { entered = true; continue; }
if !entered { break; }
if !quoted && c == ')' { exited_cleanly = true; if !arg.is_empty() { args.push(arg.clone()); arg.clear(); } break; }
if c == '\"' && !escaped { quoted = !quoted; // either fucked or empty string if !quoted && arg.len() == 0 { args.push("".into()); }
continue; }
if c == '\\' && !escaped { escaped = true; continue; }
if c == '$' && !escaped && !quoted { let ret = find_and_execute_lua_block(&tokens[consumed - 1..], proj_context, skid_context); if ret.is_some() { let (result, lua_consumed) = ret.unwrap(); consumed += std::cmp::max(lua_consumed, 1) - 1; for t in &result { arg.push(t.contents); } } else { arg.push('$'); } continue; }
if c.is_whitespace() && !quoted { if !arg.is_empty() { args.push(arg.clone()); arg.clear(); } continue; } arg.push(c); }
if !entered || !exited_cleanly { return None; } return Some((args, consumed));}
pub fn collect_block(tokens: &[Token]) -> Option<(Vec<Token>, usize)> { let mut entered = false; let mut tokens_consumed: usize = 0; let mut entering_bracket_count = 0; let mut exiting_bracket_count = 0; let mut scope_count = 0; //incremented by '{{{', decremented by '}}}' let mut escaped = false;
let mut block: Vec<Token> = Vec::new();
// We dont really care about doing anything that in the block right now // maybe have the Token struct contain scope level later? let mut escaped_tok: Token = Token::new('\\', 0, 0); for tok in tokens { tokens_consumed += 1; if !entered { if tok.contents.is_whitespace() { continue; } if tok.contents != '{' // Expected block start, got garbage { // println!("Expected block start, got {}",tok.contents); // for t in &block // { // print!("{} ", t.contents); // } // exit(1); return None; } }
let mut escaped_used = false;
// Scope Start if tok.contents == '{' && !escaped { entering_bracket_count += 1;
if entering_bracket_count == 3 { scope_count += 1; entering_bracket_count = 0; if !entered { entered = true; } } } else { entering_bracket_count = 0; if escaped { escaped_used = true; } } // Scope End if tok.contents == '}' && !escaped { exiting_bracket_count += 1; if exiting_bracket_count == 3 { scope_count -= 1; entering_bracket_count = 0; } if scope_count == 0 { break; } } else { exiting_bracket_count = 0; if escaped { escaped_used = true; } }
if escaped_used { escaped = false; block.push(escaped_tok.clone()); }
if tok.contents == '\\' { escaped = true; escaped_tok = tok.clone(); } else { block.push(tok.clone()); } }
if scope_count != 0 { return None; }
// if block.len() == 6 // // things get ugly if its empty // { // let mut emptyblock = Vec::new(); // emptyblock.push(Token::new( // "".into(), // tokens[0].origin_file, // tokens[0].line_number, // )); // return (emptyblock, tokens_consumed); // } // pop brackets, bad and ugly but idgaf block.drain(..3); block.drain(block.len() - 2..); return Some((block, tokens_consumed));}
// Call this when you think it might be a lua block, ie $ is the current token's content// (Output, consumed)pub fn find_and_execute_lua_block( tokens: &[Token], proj_context: &mut Project, skid_context: &mut SkidContext,) -> Option<(Vec<Token>, usize)> { if tokens.len() < 3 { return None; } if tokens[0].contents != '$' || tokens[1].contents != '[' { return None; }
#[derive(PartialEq)] enum QuoteType { UnQuoted, DoubledQuoutes, SingleQuotes, // Lua also has [[...]] strings for multiline but we don't need to specially keep track of them }
let mut quoted = QuoteType::UnQuoted; let mut escaped = false; let mut brackets = 0; let mut consumed: usize = 1; let mut exited_cleanly = false; // Look for the end of th $[] block for t in &tokens[1..] { consumed += 1;
if quoted == QuoteType::UnQuoted && t.contents == '[' && !escaped { brackets += 1; continue; }
if quoted == QuoteType::UnQuoted && t.contents == ']' && !escaped { brackets -= 1; if brackets == 0 { exited_cleanly = true; break; } continue; }
if quoted == QuoteType::UnQuoted && t.contents == '"' && !escaped { quoted = QuoteType::DoubledQuoutes; continue; }
if quoted == QuoteType::UnQuoted && t.contents == '\'' && !escaped { quoted = QuoteType::SingleQuotes; continue; }
if quoted == QuoteType::DoubledQuoutes && t.contents == '"' && !escaped { quoted = QuoteType::UnQuoted; continue; }
if quoted == QuoteType::SingleQuotes && t.contents == '\'' && !escaped { quoted = QuoteType::UnQuoted; continue; }
if t.contents == '\\' && !escaped { escaped = true; continue; }
if escaped { escaped = false; } }
if !exited_cleanly { return None; }
// Process embeded macros first let out = process_skid(&tokens[2..consumed - 1], proj_context, skid_context); let trimmed = trim_whitespace_tokens(&out); let mut string: String = "".to_string(); for t in trimmed { string.push(t.contents); }
let ret: Result<mlua::Value, mlua::Error> = skid_context.lua.load(string).eval(); if ret.is_err() { error_skid( proj_context, tokens[0].template_origin, tokens[0].origin_line, &ret.err()?.to_string(), ); return None; }
if ret.as_ref().unwrap().is_nil() { return Some((Vec::new(), consumed)); }
let mut return_tokens = split_to_tokens(ret.unwrap().to_string().unwrap(), tokens[0].origin_index); for t in &mut return_tokens { t.pre_proccessed = true; } Some((return_tokens, consumed))}
// Theres no std function to have the delimiters be their own element in the out vector// clean it up a bit herepub fn split_keep_delimiters(instr: String) -> Vec<String> { let split: Vec<&str> = instr.split_inclusive(DELIMITERS).collect(); let mut output = Vec::new();
for s in split { if s.ends_with(DELIMITERS) { let (token, ending) = s.split_at(s.len() - 1); if token.len() > 0 { output.push(token.to_string()); } output.push(ending.to_string()); //println!("({}, {})", token.to_string(), ending.to_string()) } else { output.push(s.to_string()); } } return output;}
pub fn strings_to_tokens(in_strings: Vec<String>, origin_file: usize) -> Vec<Token> { let mut tokens = Vec::new(); let mut line_count = 1;
for str in in_strings { for c in str.chars() { let current_line = line_count; for char in str.chars() { if char == '\n' { line_count += 1; } } let token: Token = Token::new(c, origin_file, current_line); tokens.push(token); } }
return tokens;}
// Need to do some special case stuff so you can macros without spaces between// (something like "stuff!insert(..)" is split to ["stuff","!insert(..)"] so it can be acted on later)pub fn split_to_tokens(instr: String, origin_file: usize) -> Vec<Token> { let split = split_keep_delimiters(instr); let mut new_split: Vec<String> = Vec::new(); for s in split { let prefix_offset = s.find(&['!', '&']); if prefix_offset.is_some() { let (first, second) = s.split_at(prefix_offset.unwrap()); //println!("\"{}\", \"{}\"", first, second); if first.len() > 0 { new_split.push(first.to_string()); } if second.len() > 0 { new_split.push(second.to_string()); } } else { if s.len() > 0 { new_split.push(s); } } //sleep(std::time::Duration::from_millis(10)); } return strings_to_tokens(new_split, origin_file);}
pub fn next_nonwhitespace_token(tokens: &Vec<Token>, index: usize) -> Option<usize> { while index < tokens.len() { if tokens[index].contents.is_whitespace() { continue; } return Some(index); } return None;}
//trim whitespace from the endspub fn trim_whitespace_tokens(tokens: &[Token]) -> &[Token] { if tokens.len() == 0 { return tokens; }
let mut start: usize = 0; let mut end: usize = tokens.len(); for tok in tokens { if !tok.contents.is_whitespace() { break; } start = start + 1; }
for tok in tokens.iter().rev() { if !tok.contents.is_whitespace() { break; } end = end - 1; }
return &tokens[start..end];}
// Find the first instance of the patternpub fn find_pattern(tokens: &[Token], pat: String) -> Option<(usize, usize)> { // (startpoint, length)
let split_pattern = split_to_tokens(pat, 0); let mut pattern_index: usize = 0; let mut token_index: usize = 0;
while token_index < tokens.len() && tokens.len() - token_index >= split_pattern.len() { for t in &tokens[token_index..] { if t.contents == split_pattern[pattern_index].contents { pattern_index += 1; if pattern_index == split_pattern.len() { return Some((token_index, split_pattern.len())); } } else { pattern_index = 0; token_index += 1; break; } } }
None}
pub trait WhitespaceChecks { fn is_only_whitespace(&self) -> bool; fn contains_whitespace(&self) -> bool;}
impl WhitespaceChecks for String { fn is_only_whitespace(&self) -> bool { for c in self.chars() { if !c.is_whitespace() { return false; } } return true; }
fn contains_whitespace(&self) -> bool { for c in self.chars() { if c.is_whitespace() { return true; } } return false; }}
pub trait TokenTools { fn trim_whitespace(&mut self) -> &[Token];}
impl TokenTools for Vec<Token> { fn trim_whitespace(&mut self) -> &[Token] { return trim_whitespace_tokens(&self[..]); }}