1use std::{borrow::Cow, collections::{HashMap, HashSet}, fmt::{Display, Write}};
101
102use regex::{Captures, Regex};
103
104use crate::{error::{ParseError, Result}, expression::{Expression, ExpressionType}, expression_tokenizer::{Token, TokenType}};
105
106pub enum Local{
108 As(String),
110 This,
112 None
114}
115
116pub struct Scope{
118 pub opened: Box<dyn Block>,
120 pub depth: usize
122}
123
124enum PendingWrite<'a>{
126 Raw(&'a str),
128 Expression((Expression<'a>, &'static str, &'static str)),
130 Format((&'a str, &'a str, &'a str))
131}
132
133pub struct Rust{
135 pub using: HashSet<String>,
137 pub code: String
139}
140
141pub static USE_AS_DISPLAY: &str = "AsDisplay";
143pub static USE_AS_DISPLAY_HTML: &str = "AsDisplayHtml";
145
146pub struct Uses<'a>{
148 uses: &'a HashSet<String>,
149 crate_name: &'a str
150}
151
152impl<'a> Display for Uses<'a>{
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 match self.uses.len(){
155 0 => (),
156 1 => write!(f, "use {}::{}", self.crate_name, self.uses.iter().next().unwrap())?,
157 _ => {
158 f.write_str("use ")?;
159 f.write_str(&self.crate_name)?;
160 f.write_str("::")?;
161 let mut glue = '{';
162 for use_ in self.uses{
163 f.write_char(glue)?;
164 f.write_str(use_)?;
165 glue = ',';
166 }
167 f.write_str("}")?;
168 }
169 }
170 Ok(())
171 }
172}
173
174impl Rust{
175 pub fn new() -> Self{
177 Self{
178 using: HashSet::new(),
179 code: String::new()
180 }
181 }
182
183 pub fn uses<'a>(&'a self, crate_name: &'a str) -> Uses<'a>{
185 Uses{ uses: &self.using, crate_name}
186 }
187}
188
189pub trait Block{
191 fn handle_close<'a>(&self, rust: &mut Rust) {
193 rust.code.push_str("}");
194 }
195
196 fn resolve_private<'a>(&self, _depth: usize, expression: &'a Expression<'a>, _name: &str, _rust: &mut Rust) -> Result<()>{
198 Err(ParseError::new(&format!("{} not expected ", expression.content), expression))
199 }
200
201 fn handle_else<'a>(&self, expression: &'a Expression<'a>, _rust: &mut Rust) -> Result<()>{
203 Err(ParseError::new("else not expected here", expression))
204 }
205
206 fn this<'a>(&self) -> Option<&str>{
208 None
209 }
210
211 fn local<'a>(&self) -> &Local{
213 &Local::None
214 }
215}
216
217pub trait BlockFactory{
219 fn open<'a>(&self, compile: &'a Compile<'a>, token: Token<'a>, expression: &'a Expression<'a>, rust: &mut Rust) -> Result<Box<dyn Block>>;
221}
222
223pub type BlockMap = HashMap<&'static str, &'static dyn BlockFactory>;
225
226pub struct Compile<'a>{
228 pub open_stack: Vec<Scope>,
230 pub block_map: &'a BlockMap
232}
233
234pub fn append_with_depth(depth: usize, var: &str, buffer: &mut String){
236 buffer.push_str(var);
237 buffer.push('_');
238 buffer.push_str(depth.to_string().as_str());
239}
240
241struct Root<'a>{
243 this: Option<&'a str>
244}
245
246impl<'a> Block for Root<'a>{
247 fn this<'b>(&self) -> Option<&str>{
248 self.this
249 }
250}
251
252impl<'a> Compile<'a>{
253 fn new(this: Option<&'static str>, block_map: &'a BlockMap) -> Self{
255 Self{
256 open_stack: vec![Scope{
257 depth: 0,
258 opened: Box::new(Root{this})
259 }],
260 block_map
261 }
262 }
263
264 fn find_scope(&self, var: &'a str) -> Result<(&'a str, &Scope)>{
266 let mut scope = self.open_stack.last().unwrap();
267 let mut local = var;
268 while local.starts_with("../"){
269 match scope.depth{
270 0 => return Err(ParseError{ message: format!("unable to resolve scope for {}", var)}),
271 _ => {
272 local = &local[3 ..];
273 scope = self.open_stack.get(scope.depth - 1).unwrap();
274 }
275 }
276 }
277 return Ok((local, scope));
278 }
279
280 fn resolve_local(&self, depth: usize, var: &'a str, local: &'a str, buffer: &mut String) -> bool{
282 if var.starts_with(local){
283 let len = local.len();
284 if var.len() > len{
285 if &var[len .. len + 1] != "."{
286 return false;
287 }
288 append_with_depth(depth, local, buffer);
289 buffer.push_str(&var[len ..]);
290 }
291 else{
292 append_with_depth(depth, local, buffer);
293 }
294 return true;
295 }
296 return false;
297 }
298
299 fn resolve_var(&self, var: &'a str, scope: &Scope, buffer: &mut String) -> Result<()>{
301 if scope.depth == 0{
302 if let Some(this) = scope.opened.this(){
303 buffer.push_str(this);
304 buffer.push('.');
305 }
306 buffer.push_str(var);
307 return Ok(());
308 }
309 if match scope.opened.local(){
310 Local::As(local) => self.resolve_local(scope.depth, var, local, buffer),
311 Local::This => {
312 buffer.push_str("this_");
313 buffer.push_str(scope.depth.to_string().as_str());
314 if var != "this"{
315 buffer.push('.');
316 buffer.push_str(var);
317 }
318 true
319 },
320 Local::None => false
321 }{
322 return Ok(());
323 }
324 let parent = &self.open_stack[scope.depth - 1];
325 if let Some(this) = scope.opened.this(){
326 self.resolve_var(this, parent, buffer)?;
327 if var != this{
328 buffer.push('.');
329 buffer.push_str(var);
330 }
331 }
332 else{
333 self.resolve_var(var, parent, buffer)?;
334 }
335 Ok(())
336 }
337
338 fn resolve_sub_expression(&self, raw: &str, value: &str, rust: &mut Rust) -> Result<()>{
340 self.resolve(&Expression {
341 expression_type: ExpressionType::Raw,
342 prefix: "",
343 content: value,
344 postfix: "",
345 raw
346 }, rust)
347 }
348
349 pub fn write_var(&self, expression: &Expression<'a>, rust: &mut Rust, var: &Token<'a>) -> Result<()>{
351 match var.token_type{
352 TokenType::PrivateVariable => {
353 let (name, scope) = self.find_scope(var.value)?;
354 scope.opened.resolve_private(scope.depth, expression, name, rust)?;
355 },
356 TokenType::Variable => {
357 let (name, scope) = self.find_scope(var.value)?;
358 self.resolve_var(name, scope, &mut rust.code)?;
359 },
360 TokenType::Literal => {
361 rust.code.push_str(var.value);
362 },
363 TokenType::SubExpression(raw) => {
364 self.resolve_sub_expression(raw, var.value, rust)?;
365 }
366 }
367 Ok(())
368 }
369
370 fn handle_else(&self, expression: &Expression<'a>, rust: &mut Rust) -> Result<()>{
372 match self.open_stack.last() {
373 Some(scope) => scope.opened.handle_else(expression, rust),
374 None => Err(ParseError::new("else not expected here", expression))
375 }
376 }
377
378 fn resolve_lookup(&self, expression: &Expression<'a>, prefix: &str, postfix: char, args: Token<'a>, rust: &mut Rust) -> Result<()>{
380 self.write_var(expression, rust, &args)?;
381 rust.code.push_str(prefix);
382 self.write_var(expression, rust, &args.next()?.ok_or(
383 ParseError::new("lookup expects 2 arguments", &expression))?
384 )?;
385 rust.code.push(postfix);
386 Ok(())
387 }
388
389 fn resolve_helper(&self, expression: &Expression<'a>, name: Token<'a>, mut args: Token<'a>, rust: &mut Rust) -> Result<()>{
391 match name.value{
392 "lookup" => self.resolve_lookup(expression, "[", ']', args, rust),
393 "try_lookup" => self.resolve_lookup(expression, ".get(", ')', args, rust),
394 name => {
395 rust.code.push_str(name);
396 rust.code.push('(');
397 self.write_var(expression, rust, &args)?;
398 loop {
399 args = match args.next()?{
400 Some(token) => {
401 rust.code.push_str(", ");
402 self.write_var(expression, rust, &token)?;
403 token
404 },
405 None => {
406 rust.code.push(')');
407 return Ok(());
408 }
409 };
410 }
411 }
412 }
413 }
414
415 fn resolve(&self, expression: &Expression<'a>, rust: &mut Rust) -> Result<()>{
417 let token = match Token::first(&expression.content)?{
418 Some(token) => token,
419 None => return Err(ParseError::new("expected token", &expression))
420 };
421 rust.code.push_str(expression.prefix);
422 if let TokenType::SubExpression(raw) = token.token_type{
423 self.resolve_sub_expression(raw, token.value, rust)?;
424 }
425 else if let Some(args) = token.next()?{
426 self.resolve_helper(expression, token, args, rust)?;
427 }
428 else{
429 self.write_var(expression, rust, &token)?;
430 }
431 rust.code.push_str(expression.postfix);
432 Ok(())
433 }
434
435 pub fn write_local(&self, rust: &mut String, local: &Local){
437 append_with_depth(self.open_stack.len(), match local{
438 Local::As(local) => local,
439 _ => "this"
440 }, rust);
441 }
442
443 fn close(&mut self, expression: Expression<'a>, rust: &mut Rust) -> Result<()>{
445 let scope = self.open_stack.pop().ok_or_else(|| ParseError::new("Mismatched block helper", &expression))?;
446 Ok(scope.opened.handle_close(rust))
447 }
448
449 fn open(&mut self, expression: Expression<'a>, rust: &mut Rust) -> Result<()>{
451 let token = Token::first(&expression.content)?.ok_or_else(|| ParseError::new("expected token", &expression))?;
452 match self.block_map.get(token.value){
453 Some(block) => {
454 self.open_stack.push(Scope{
455 opened: block.open(self, token, &expression, rust)?,
456 depth: self.open_stack.len()
457 });
458 Ok(())
459 },
460 None => Err(ParseError::new(&format!("unsupported block helper {}", token.value), &expression))
461 }
462 }
463}
464
465#[derive(Debug, Clone, Copy)]
467pub struct Options{
468 pub root_var_name: Option<&'static str>,
470 pub write_var_name: &'static str
472}
473
474pub struct Compiler{
476 clean: Regex,
478 options: Options,
480 block_map: BlockMap
482}
483
484impl Compiler {
485 pub fn new(options: Options, block_map: BlockMap) -> Self{
487 Self{
488 clean: Regex::new("[\\\\\"\\{\\}]").unwrap(),
489 options,
490 block_map
491 }
492 }
493
494 fn escape<'a>(&self, content: &'a str) -> Cow<'a, str> {
496 self.clean.replace_all(
497 &content, |captures: &Captures| match &captures[0]{
498 "{" | "}" => format!("{}{}", &captures[0], &captures[0]),
499 _ => format!("\\{}", &captures[0])
500 }
501 )
502 }
503
504 fn commit_pending<'a>(&self, pending: &mut Vec<PendingWrite<'a>>, compile: &mut Compile<'a>, rust: &mut Rust) -> Result<()>{
506 if pending.is_empty(){
507 return Ok(());
508 }
509 rust.code.push_str("write!(");
510 rust.code.push_str(self.options.write_var_name);
511 rust.code.push_str(", \"");
512 for pending in pending.iter(){
513 match pending{
514 PendingWrite::Raw(raw) => rust.code.push_str(self.escape(raw).as_ref()),
515 PendingWrite::Expression(_) => rust.code.push_str("{}"),
516 PendingWrite::Format((_, format, _)) => rust.code.push_str(format)
517 }
518 }
519 rust.code.push('"');
520 for pending in pending.iter(){
521 match pending{
522 PendingWrite::Expression((expression, uses, display)) => {
523 compile.resolve(&Expression{
524 expression_type: ExpressionType::Raw,
525 prefix: ", ",
526 content: expression.content,
527 postfix: display,
528 raw: expression.raw
529 }, rust)?;
530 rust.using.insert(uses.to_string());
531 },
532 PendingWrite::Format((raw, _, content)) => {
533 compile.resolve(&Expression{
534 expression_type: ExpressionType::Raw,
535 prefix: ", ",
536 content,
537 postfix: "",
538 raw
539 }, rust)?;
540 },
541 _ => ()
542 }
543 }
544 rust.code.push_str(")?;");
545 pending.clear();
546 Ok(())
547 }
548
549 fn select_write<'a>(expression: &Expression<'a>, uses: &'static str, postfix: &'static str) -> Result<PendingWrite<'a>>{
550 if let Some(token) = Token::first(&expression.content)?{
551 if let TokenType::Variable = token.token_type{
552 if token.value != "format"{
553 return Ok(PendingWrite::Expression((expression.clone(), uses, postfix)));
554 }
555 let pattern = match token.next()?{
556 Some(token) => token,
557 _ => return Ok(PendingWrite::Expression((expression.clone(), uses, postfix)))
558 };
559 let value = match pattern.next(){
560 Ok(Some(token)) => token,
561 _ => return Err(ParseError::new("format requires 2 arguments", expression))
562 };
563 if let TokenType::Literal = pattern.token_type{
564 if pattern.value.starts_with('"') && pattern.value.ends_with('"'){
565 return Ok(PendingWrite::Format((expression.raw, &pattern.value[1..pattern.value.len() - 1], value.value)));
566 }
567 }
568 return Err(ParseError::new("first argument of format must be a string literal", expression));
569 }
570 }
571 Ok(PendingWrite::Expression((expression.clone(), uses, postfix)))
572 }
573
574 pub fn compile(&self, src: &str) -> Result<Rust>{
576 let mut compile = Compile::new(self.options.root_var_name, &self.block_map);
577 let mut rust = Rust::new();
578 let mut pending: Vec<PendingWrite> = Vec::new();
579 let mut rest = src;
580 let mut expression = Expression::from(src)?;
581 while let Some(expr) = expression{
582 let Expression{
583 expression_type,
584 prefix,
585 content,
586 postfix,
587 raw: _
588 } = &expr;
589 rest = postfix;
590 if !prefix.is_empty(){
591 pending.push(PendingWrite::Raw(prefix));
592 }
593 match expression_type{
594 ExpressionType::Raw => pending.push(Self::select_write(&expr, USE_AS_DISPLAY, ".as_display()")?),
595 ExpressionType::HtmlEscaped => if *content == "else" {
596 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
597 compile.handle_else(&expr, &mut rust)?
598 } else {
599 pending.push(Self::select_write(&expr, USE_AS_DISPLAY_HTML, ".as_display_html()")?)
600 },
601 ExpressionType::Open => {
602 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
603 compile.open(expr, &mut rust)?
604 },
605 ExpressionType::Close => {
606 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
607 compile.close(expr, &mut rust)?
608 },
609 ExpressionType::Escaped => pending.push(PendingWrite::Raw(content)),
610 _ => ()
611 };
612 expression = expr.next()?;
613 }
614 if !rest.is_empty(){
615 pending.push(PendingWrite::Raw(rest));
616 }
617 self.commit_pending(&mut pending, &mut compile, &mut rust)?;
618 Ok(rust)
619 }
620}