Tholp's bespoke website generator
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}