Tholp's bespoke website generator
at main 8.2 kB view raw
1use crate::{ 2 console::error_skid, 3 project::Project, 4 reservednames::{RESERVED_NAMES_HTML, RESERVED_NAMES_MISC}, 5 stringtools::{find_pattern, split_to_tokens, WhitespaceChecks}, 6 types::{IsScoped, SkidContext, Token}, 7}; 8 9use super::MACRO_LIST; 10 11pub struct SkidTemplate { 12 pub symbol: String, 13 pub args: Vec<String>, 14 pub tokens: Vec<Token>, 15 16 pub has_scope: bool, 17 pub allows_trailing_args: bool, 18 19 pub origin_index: usize, 20 pub origin_line: usize, 21} 22 23impl SkidTemplate { 24 pub fn new( 25 name: String, 26 args: &[String], 27 tokens: &[Token], 28 origin_index: usize, 29 origin_line: usize, 30 ) -> SkidTemplate { 31 let scoped: bool = find_pattern(&tokens, "[[{}]]".into()).is_some(); 32 let trailing: bool = find_pattern(&tokens, "[[..]]".into()).is_some() 33 || find_pattern(&tokens, "[[\"..\"]]".into()).is_some(); 34 35 SkidTemplate { 36 symbol: name, 37 args: args.to_vec(), 38 tokens: tokens.to_vec(), 39 has_scope: scoped, 40 allows_trailing_args: trailing, 41 origin_index, 42 origin_line, 43 } 44 } 45 pub fn expand( 46 &self, 47 //_file: &mut InputFile, 48 origin_index: usize, 49 origin_line: usize, 50 proj_context: &mut Project, 51 args: &Vec<String>, 52 scope: &[Token], 53 ) -> Vec<Token> { 54 //println!("{:?}", args); 55 56 if !self.allows_trailing_args && args.len() != self.args.len() { 57 error_skid( 58 proj_context, 59 origin_index, 60 origin_line, 61 &format!( 62 "Template \"{}\" requires exactly {} arguments, got given {} ({:?})", 63 self.symbol, 64 self.args.len(), 65 args.len(), 66 args 67 ), 68 ); 69 } 70 if self.allows_trailing_args && args.len() < self.args.len() { 71 error_skid( 72 proj_context, 73 origin_index, 74 origin_line, 75 &format!( 76 "Template \"{}\" requires at least {} arguments, got given {} ({:?})", 77 self.symbol, 78 self.args.len(), 79 args.len(), 80 args 81 ), 82 ); 83 } 84 85 let mut output = self.tokens.clone(); 86 87 for tok in &mut output { 88 tok.origin_index = origin_index; 89 } 90 91 let mut args_index: usize = 0; 92 for param in &self.args { 93 let mut found_pattern = find_pattern(&output, format!("[[{}]]", param)); 94 while found_pattern.is_some() { 95 let (start, len) = found_pattern.unwrap(); 96 let replacement = split_to_tokens(args[args_index].clone(), origin_index); 97 output.splice(start..start + len, replacement); 98 found_pattern = find_pattern(&output, format!("[[{}]]", param)); 99 } 100 args_index += 1; 101 } 102 103 //replace [[..]] with space seperated remaining args 104 let mut found_trailing_pattern = find_pattern(&output, "[[..]]".into()); 105 while found_trailing_pattern.is_some() { 106 let (start, len) = found_trailing_pattern.unwrap(); 107 let mut replacement = Vec::new(); 108 for arg in &args[self.args.len()..] { 109 replacement.append(&mut split_to_tokens(arg.clone() + " ".into(), origin_index)); 110 } 111 output.splice(start..start + len, replacement); 112 found_trailing_pattern = find_pattern(&output, "[[..]]".into()); 113 } 114 115 //replace [[".."]] with space seperated quoted remaining args 116 found_trailing_pattern = find_pattern(&output, "[[\"..\"]]".into()); 117 while found_trailing_pattern.is_some() { 118 let (start, len) = found_trailing_pattern.unwrap(); 119 let mut replacement = Vec::new(); 120 for arg in &args[self.args.len()..] { 121 replacement.append(&mut split_to_tokens( 122 "\"".to_string() + arg + "\" ".into(), 123 origin_index, 124 )); 125 } 126 output.splice(start..start + len, replacement); 127 found_trailing_pattern = find_pattern(&output, "[[\"..\"]]".into()); 128 } 129 130 let mut found_block_pattern = find_pattern(&output, "[[{}]]".into()); 131 while found_block_pattern.is_some() { 132 let (start, len) = found_block_pattern.unwrap(); 133 let replacement = scope.to_vec(); 134 output.splice(start..start + len, replacement); 135 found_block_pattern = find_pattern(&output, "[[{}]]".into()); 136 } 137 138 output 139 } 140} 141 142impl IsScoped for SkidTemplate { 143 fn is_scoped(&self) -> bool { 144 self.has_scope 145 } 146} 147 148pub fn macro_template( 149 origin_index: usize, 150 origin_line: usize, 151 project_context: &mut Project, 152 skid_context: &mut SkidContext, 153 args: &Vec<String>, 154 scope: &[Token], 155) -> Vec<Token> { 156 for t in skid_context.templates.iter().as_ref() { 157 if t.symbol == args[0] { 158 // If its the same file and line then we know its the same exact thing, just skip over it 159 if t.origin_index == origin_index && t.origin_line == origin_line { 160 return Vec::new(); 161 } 162 error_skid( 163 project_context, 164 origin_index, 165 origin_line, 166 &format!("Attempted template redefinition of \"{}\"", args[0]), 167 ); 168 } 169 } 170 171 for t in MACRO_LIST { 172 if t.symbol == args[0] { 173 error_skid( 174 project_context, 175 origin_index, 176 origin_line, 177 &format!( 178 "Attempted to make a template using a reserved name \"{}\"", 179 args[0] 180 ), 181 ); 182 } 183 } 184 185 for r in RESERVED_NAMES_HTML { 186 if **r == args[0] { 187 error_skid( 188 project_context, 189 origin_index, 190 origin_line, 191 &format!( 192 "Attempted to make a template using a reserved name \"{}\"", 193 r 194 ), 195 ); 196 } 197 } 198 199 for r in RESERVED_NAMES_MISC { 200 if **r == args[0] { 201 error_skid( 202 project_context, 203 origin_index, 204 origin_line, 205 &format!( 206 "Attempted to make a template using a reserved name \"{}\"", 207 r 208 ), 209 ); 210 } 211 } 212 213 for arg in args { 214 if arg == ".." || arg == "\"..\"" { 215 error_skid( 216 project_context, 217 origin_index, 218 origin_line, 219 &format!( 220 "Attempted to make a template using a reserved parameter name \"{}\"", 221 arg 222 ), 223 ); 224 } 225 } 226 227 let mut used_params = 0; 228 for param in &args[1..] { 229 if find_pattern(scope, format!("[[{}]]", param)).is_some() { 230 used_params += 1; 231 } 232 if param.contains_whitespace() { 233 error_skid( 234 project_context, 235 origin_index, 236 origin_line, 237 &format!( 238 "Attempted to make a template with a parameter that contains whitespace \"{}\"", 239 param 240 ), 241 ); 242 } 243 } 244 245 if used_params < args.len() - 1 { 246 error_skid( 247 project_context, 248 origin_index, 249 origin_line, 250 &format!( 251 "Template definition of \"{}\" has {} paramters but only uses {}", 252 args[0], 253 args.len() - 1, 254 used_params 255 ), 256 ); 257 } 258 259 let template = SkidTemplate::new( 260 args[0].clone(), 261 &args[1..], 262 scope, 263 origin_index, 264 origin_line, 265 ); 266 skid_context.templates.push(template); 267 268 return Vec::new(); 269}