1use crate::{compiler::{append_with_depth, Block, BlockFactory, BlockMap, Compile, Local, Rust}, error::{ParseError, Result}, expression::{Expression, ExpressionType}, expression_tokenizer::Token};
42
43fn strip_pipes<'a>(token: Token<'a>, expression: &Expression<'a>) -> Result<&'a str> {
45 loop {
46 match token.next()? {
47 Some(token) => {
48 if token.value == "|" {
49 continue;
50 }
51 return Ok(token.value.trim_matches('|'));
52 },
53 None => return Err(ParseError::new("expected variable after as", expression))
54 }
55 }
56}
57
58fn read_local<'a>(token: &Token<'a>, expression: &Expression<'a>) -> Result<Local> {
60 match token.next()? {
61 Some(token) => {
62 match token.value {
63 "as" => Ok(Local::As(strip_pipes(token, expression)?.to_string())),
64 token => Err(ParseError::new(&format!("unexpected token {}", token), expression))
65 }
66 },
67 None => Ok(Local::This)
68 }
69}
70
71struct IfOrUnless {}
73
74impl IfOrUnless {
75 pub fn new<'a>(label: &str, prefix: &str, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<IfOrUnless> {
77 match token.next()? {
78 Some(var) => {
79 rust.using.insert("AsBool".to_string());
80 rust.code.push_str(prefix);
81 compile.write_var(expression, rust, &var)?;
82 rust.code.push_str(".as_bool(){");
83 Ok(Self{})
84 },
85 None => Err(ParseError::new(&format!("expected variable after {}", label), expression))
86 }
87 }
88}
89
90impl Block for IfOrUnless {
91 fn handle_else<'a>(&self, _expression: &'a Expression<'a>, rust: &mut Rust) -> Result<()> {
93 rust.code.push_str("}else{");
94 Ok(())
95 }
96}
97
98struct IfFty {}
100
101impl BlockFactory for IfFty {
102 fn open<'a>(&self, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Box<dyn Block>> {
104 Ok(Box::new(IfOrUnless::new("if", "if ", compile, token, expression, rust)?))
105 }
106}
107
108struct UnlessFty {}
110
111impl BlockFactory for UnlessFty {
112 fn open<'a>(&self, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Box<dyn Block>> {
114 Ok(Box::new(IfOrUnless::new("unless", "if !", compile, token, expression, rust)?))
115 }
116}
117
118struct IfSome {
120 local: Local
121}
122
123impl IfSome {
124 fn new<'a>(by_ref: bool, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Self> {
126 let next = token.next()?.ok_or_else(|| ParseError::new(
127 &format!("expected variable after if_some{}", if by_ref {"_ref"} else {""}), expression
128 ))?;
129 let local = read_local(&next, expression)?;
130 rust.code.push_str("if let Some(");
131 compile.write_local(&mut rust.code, &local);
132 rust.code.push_str(") = ");
133 if by_ref {
134 rust.code.push('&');
135 }
136 compile.write_var(expression, rust, &next)?;
137 rust.code.push('{');
138 Ok(Self{local})
139 }
140}
141
142impl Block for IfSome {
143 fn handle_else<'a>(&self, _expression: &'a Expression<'a>, rust: &mut Rust) -> Result<()> {
145 rust.code.push_str("}else{");
146 Ok(())
147 }
148
149 fn local<'a>(&self) -> &Local {
151 &self.local
152 }
153}
154
155struct IfSomeFty {}
157
158impl BlockFactory for IfSomeFty {
159 fn open<'a>(&self, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Box<dyn Block>> {
161 Ok(Box::new(IfSome::new(false, compile, token, expression, rust)?))
162 }
163}
164
165struct IfSomeRefFty {}
167
168impl BlockFactory for IfSomeRefFty {
169 fn open<'a>(&self, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Box<dyn Block>> {
171 Ok(Box::new(IfSome::new(true, compile, token, expression, rust)?))
172 }
173}
174
175struct With {
177 local: Local
178}
179
180impl With {
181 pub fn new<'a>(by_ref: bool, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Self> {
183 let next = token.next()?.ok_or_else(|| ParseError::new(
184 &format!("expected variable after with{}", if by_ref {"_ref"} else {""}), expression
185 ))?;
186 let local = read_local(&next, expression)?;
187 rust.code.push_str("{let ");
188 compile.write_local(&mut rust.code, &local);
189 rust.code.push_str(" = ");
190 if by_ref {
191 rust.code.push('&');
192 }
193 compile.write_var(expression, rust, &next)?;
194 rust.code.push(';');
195 Ok(Self{local})
196 }
197}
198
199impl Block for With {
200 fn local<'a>(&self) -> &Local {
202 &self.local
203 }
204}
205
206struct WithFty {}
208
209impl BlockFactory for WithFty {
210 fn open<'a>(&self, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Box<dyn Block>> {
212 Ok(Box::new(With::new(false, compile, token, expression, rust)?))
213 }
214}
215
216struct WithRefFty {}
218
219impl BlockFactory for WithRefFty {
220 fn open<'a>(&self, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Box<dyn Block>> {
222 Ok(Box::new(With::new(true, compile, token, expression, rust)?))
223 }
224}
225
226struct Each {
228 local: Local,
229 indexer: Option<String>,
230 has_else: bool
231}
232
233fn contains_indexer(src: &str, mut depth: i32) -> bool {
235 match src.find("index") {
236 Some(pos) => {
237 match src[..pos].rfind('@') {
238 Some(start) => {
239 let mut prefix = &src[start + 1 .. pos];
240 while prefix.starts_with("../") {
241 depth -= 1;
242 prefix = &prefix[3 ..];
243 }
244 return depth == 0;
245 },
246 None => return false
247 }
248 },
249 None => return false
250 }
251}
252
253fn check_for_indexer(src: &str) -> Result<bool> {
255 let mut exp = Expression::from(src)?;
256 let mut depth = 1;
257 while let Some(expr) = &exp {
258 match expr.expression_type {
259 ExpressionType::Comment | ExpressionType::Escaped => continue,
260 ExpressionType::Open => if contains_indexer(expr.content, depth - 1) {
261 return Ok(true);
262 } else {
263 depth += 1;
264 },
265 ExpressionType::Close => {
266 depth -= 1;
267 if depth == 0 {
268 return Ok(false);
269 }
270 },
271 _ => if contains_indexer(expr.content, depth - 1) {
272 return Ok(true);
273 }
274 }
275 exp = expr.next()?;
276 }
277 Ok(false)
278}
279
280fn check_for_else(src: &str) -> Result<bool> {
282 let mut exp = Expression::from(src)?;
283 let mut depth = 1;
284 while let Some(expr) = &exp {
285 match expr.expression_type {
286 ExpressionType::Comment | ExpressionType::Escaped => continue,
287 ExpressionType::Open => depth += 1,
288 ExpressionType::Close => {
289 depth -= 1;
290 if depth == 0 {
291 return Ok(false);
292 }
293 },
294 _ => if expr.content == "else" && depth == 1 {
295 return Ok(true);
296 }
297 }
298 exp = expr.next()?;
299 }
300 Ok(false)
301}
302
303impl Each {
304 pub fn new<'a>(by_ref: bool, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Self>{
306 let next = match token.next()?{
307 Some(next) => next,
308 None => return Err(ParseError::new(&format!("expected variable after {}", if by_ref{"each_ref"}else{"each"}), expression))
309 };
310 let indexer = check_for_indexer(&expression.postfix).map(|found| match found{
311 true => {
312 let indexer = format!("i_{}", compile.open_stack.len());
313 rust.code.push_str("let mut ");
314 rust.code.push_str(indexer.as_str());
315 rust.code.push_str(" = 0;");
316 Some(indexer)
317 },
318 false => None
319 })?;
320 let local = read_local(&next, expression)?;
321 let has_else = check_for_else(&expression.postfix)?;
322 if has_else{
323 rust.code.push_str("{let mut empty = true;");
324 }
325 rust.code.push_str("for ");
326 compile.write_local(&mut rust.code, &local);
327 rust.code.push_str(" in ");
328 if by_ref{
329 rust.code.push('&');
330 }
331 compile.write_var(expression, rust, &next)?;
332 rust.code.push('{');
333 if has_else{
334 rust.code.push_str("empty = false;");
335 }
336 Ok(Self{
337 local,
338 indexer,
339 has_else
340 })
341 }
342 fn write_map_var<'a>(&self, depth: usize, suffix: &str, rust: &mut Rust) {
344 append_with_depth(depth, if let Local::As(name) = &self.local {
345 name.as_str()
346 } else {
347 "this"
348 }, &mut rust.code);
349 rust.code.push_str(suffix)
350 }
351
352 fn write_indexer<'a>(&self, rust: &mut Rust) {
354 if let Some(indexer) = &self.indexer {
355 rust.code.push_str(indexer);
356 rust.code.push_str("+=1;");
357 }
358 }
359}
360
361impl Block for Each{
362 fn handle_else<'a>(&self, _expression: &'a Expression<'a>, rust: &mut Rust) -> Result<()> {
363 self.write_indexer(rust);
364 rust.code.push_str("} if empty {");
365 Ok(())
366 }
367
368 fn resolve_private<'a>(&self, depth: usize, expression: &'a Expression<'a>, name: &str, rust: &mut Rust) -> Result<()>{
369 Ok(match name{
370 "index" => rust.code.push_str(&self.indexer.as_ref().unwrap()),
371 "key" => self.write_map_var(depth, ".0", rust),
372 "value" => self.write_map_var(depth, ".1", rust),
373 _ => Err(ParseError::new(&format!("unexpected variable {}", name), expression))?
374 })
375 }
376
377 fn handle_close<'a>(&self, rust: &mut Rust) {
378 if self.has_else{
379 rust.code.push_str("}}");
380 }
381 else{
382 self.write_indexer(rust);
383 rust.code.push('}');
384 }
385 }
386
387 fn local<'a>(&self) -> &Local {
388 &self.local
389 }
390}
391
392struct EachFty {}
394
395impl BlockFactory for EachFty {
396 fn open<'a>(&self, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Box<dyn Block>> {
398 Ok(Box::new(Each::new(false, compile, token, expression, rust)?))
399 }
400}
401
402struct EachRefFty {}
404
405impl BlockFactory for EachRefFty {
406 fn open<'a>(&self, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Box<dyn Block>> {
408 Ok(Box::new(Each::new(true, compile, token, expression, rust)?))
409 }
410}
411
412const IF: IfFty = IfFty{};
413const UNLESS: UnlessFty = UnlessFty{};
414const IF_SOME: IfSomeFty = IfSomeFty{};
415const IF_SOME_REF: IfSomeRefFty = IfSomeRefFty{};
416const WITH: WithFty = WithFty{};
417const WITH_REF: WithRefFty = WithRefFty{};
418const EACH: EachFty = EachFty{};
419const EACH_REF: EachRefFty = EachRefFty{};
420
421pub fn add_builtins(map: &mut BlockMap) {
423 map.insert("if", &IF);
424 map.insert("unless", &UNLESS);
425 map.insert("if_some", &IF_SOME);
426 map.insert("if_some_ref", &IF_SOME_REF);
427 map.insert("with", &WITH);
428 map.insert("with_ref", &WITH_REF);
429 map.insert("each", &EACH);
430 map.insert("each_ref", &EACH_REF);
431}