1use std::collections::BTreeMap;
2
3use crate::compiler::ast;
4use crate::compiler::instructions::{
5 Instruction, Instructions, LocalId, LOOP_FLAG_RECURSIVE, LOOP_FLAG_WITH_LOOP_VAR, MAX_LOCALS,
6};
7use crate::compiler::tokens::Span;
8use crate::output::CaptureMode;
9use crate::value::ops::neg;
10use crate::value::{Kwargs, UndefinedType, Value, ValueMap, ValueRepr};
11
12#[cfg(test)]
13use similar_asserts::assert_eq;
14
15#[cfg(feature = "macros")]
16type Caller<'source> = ast::Spanned<ast::Macro<'source>>;
17
18#[cfg(not(feature = "macros"))]
19type Caller<'source> = std::marker::PhantomData<&'source ()>;
20
21fn get_local_id<'source>(ids: &mut BTreeMap<&'source str, LocalId>, name: &'source str) -> LocalId {
23 if let Some(id) = ids.get(name) {
24 *id
25 } else if ids.len() >= MAX_LOCALS {
26 !0
27 } else {
28 let next_id = ids.len() as LocalId;
29 ids.insert(name, next_id);
30 next_id
31 }
32}
33
34enum PendingBlock {
37 Branch {
38 jump_instr: u32,
39 },
40 Loop {
41 iter_instr: u32,
42 jump_instrs: Vec<u32>,
43 },
44 ScBool {
45 jump_instrs: Vec<u32>,
46 },
47}
48
49pub struct CodeGenerator<'source> {
51 instructions: Instructions<'source>,
52 blocks: BTreeMap<&'source str, Instructions<'source>>,
53 pending_block: Vec<PendingBlock>,
54 current_line: u16,
55 span_stack: Vec<Span>,
56 filter_local_ids: BTreeMap<&'source str, LocalId>,
57 test_local_ids: BTreeMap<&'source str, LocalId>,
58 raw_template_bytes: usize,
59}
60
61impl<'source> CodeGenerator<'source> {
62 pub fn new(file: &'source str, source: &'source str) -> CodeGenerator<'source> {
64 CodeGenerator {
65 instructions: Instructions::new(file, source),
66 blocks: BTreeMap::new(),
67 pending_block: Vec::with_capacity(32),
68 current_line: 0,
69 span_stack: Vec::with_capacity(32),
70 filter_local_ids: BTreeMap::new(),
71 test_local_ids: BTreeMap::new(),
72 raw_template_bytes: 0,
73 }
74 }
75
76 pub fn set_line(&mut self, lineno: u16) {
78 self.current_line = lineno;
79 }
80
81 pub fn set_line_from_span(&mut self, span: Span) {
83 self.set_line(span.start_line);
84 }
85
86 pub fn push_span(&mut self, span: Span) {
88 self.span_stack.push(span);
89 self.set_line_from_span(span);
90 }
91
92 pub fn pop_span(&mut self) {
94 self.span_stack.pop();
95 }
96
97 pub fn add(&mut self, instr: Instruction<'source>) -> u32 {
99 if let Some(span) = self.span_stack.last() {
100 if span.start_line == self.current_line {
101 return self.instructions.add_with_span(instr, *span);
102 }
103 }
104 self.instructions.add_with_line(instr, self.current_line)
105 }
106
107 pub fn add_with_span(&mut self, instr: Instruction<'source>, span: Span) -> u32 {
109 self.instructions.add_with_span(instr, span)
110 }
111
112 pub fn next_instruction(&self) -> u32 {
114 self.instructions.len() as u32
115 }
116
117 #[cfg(feature = "multi_template")]
119 fn new_subgenerator(&self) -> CodeGenerator<'source> {
120 let mut sub = CodeGenerator::new(self.instructions.name(), self.instructions.source());
121 sub.current_line = self.current_line;
122 sub.span_stack = self.span_stack.last().copied().into_iter().collect();
123 sub
124 }
125
126 #[cfg(feature = "multi_template")]
128 fn finish_subgenerator(&mut self, sub: CodeGenerator<'source>) -> Instructions<'source> {
129 self.current_line = sub.current_line;
130 let (instructions, blocks) = sub.finish();
131 self.blocks.extend(blocks);
132 instructions
133 }
134
135 pub fn start_for_loop(&mut self, with_loop_var: bool, recursive: bool) {
137 let mut flags = 0;
138 if with_loop_var {
139 flags |= LOOP_FLAG_WITH_LOOP_VAR;
140 }
141 if recursive {
142 flags |= LOOP_FLAG_RECURSIVE;
143 }
144 self.add(Instruction::PushLoop(flags));
145 let instr = self.add(Instruction::Iterate(!0));
146 self.pending_block.push(PendingBlock::Loop {
147 iter_instr: instr,
148 jump_instrs: Vec::new(),
149 });
150 }
151
152 pub fn end_for_loop(&mut self, push_did_not_iterate: bool) {
154 if let Some(PendingBlock::Loop {
155 iter_instr,
156 jump_instrs,
157 }) = self.pending_block.pop()
158 {
159 self.add(Instruction::Jump(iter_instr));
160 let loop_end = self.next_instruction();
161 if push_did_not_iterate {
162 self.add(Instruction::PushDidNotIterate);
163 };
164 self.add(Instruction::PopLoopFrame);
165 for instr in jump_instrs.into_iter().chain(Some(iter_instr)) {
166 match self.instructions.get_mut(instr) {
167 Some(&mut Instruction::Iterate(ref mut jump_target))
168 | Some(&mut Instruction::Jump(ref mut jump_target)) => {
169 *jump_target = loop_end;
170 }
171 _ => unreachable!(),
172 }
173 }
174 } else {
175 unreachable!()
176 }
177 }
178
179 pub fn start_if(&mut self) {
181 let jump_instr = self.add(Instruction::JumpIfFalse(!0));
182 self.pending_block.push(PendingBlock::Branch { jump_instr });
183 }
184
185 pub fn start_else(&mut self) {
187 let jump_instr = self.add(Instruction::Jump(!0));
188 self.end_condition(jump_instr + 1);
189 self.pending_block.push(PendingBlock::Branch { jump_instr });
190 }
191
192 pub fn end_if(&mut self) {
194 self.end_condition(self.next_instruction());
195 }
196
197 pub fn start_sc_bool(&mut self) {
199 self.pending_block.push(PendingBlock::ScBool {
200 jump_instrs: Vec::new(),
201 });
202 }
203
204 pub fn sc_bool(&mut self, and: bool) {
206 if let Some(&mut PendingBlock::ScBool {
207 ref mut jump_instrs,
208 }) = self.pending_block.last_mut()
209 {
210 jump_instrs.push(self.instructions.add(if and {
211 Instruction::JumpIfFalseOrPop(!0)
212 } else {
213 Instruction::JumpIfTrueOrPop(!0)
214 }));
215 } else {
216 unreachable!();
217 }
218 }
219
220 pub fn end_sc_bool(&mut self) {
222 let end = self.next_instruction();
223 if let Some(PendingBlock::ScBool { jump_instrs }) = self.pending_block.pop() {
224 for instr in jump_instrs {
225 match self.instructions.get_mut(instr) {
226 Some(&mut Instruction::JumpIfFalseOrPop(ref mut target))
227 | Some(&mut Instruction::JumpIfTrueOrPop(ref mut target)) => {
228 *target = end;
229 }
230 _ => unreachable!(),
231 }
232 }
233 }
234 }
235
236 fn end_condition(&mut self, new_jump_instr: u32) {
237 match self.pending_block.pop() {
238 Some(PendingBlock::Branch { jump_instr }) => {
239 match self.instructions.get_mut(jump_instr) {
240 Some(&mut Instruction::JumpIfFalse(ref mut target))
241 | Some(&mut Instruction::Jump(ref mut target)) => {
242 *target = new_jump_instr;
243 }
244 _ => {}
245 }
246 }
247 _ => unreachable!(),
248 }
249 }
250
251 pub fn compile_stmt(&mut self, stmt: &ast::Stmt<'source>) {
253 match stmt {
254 ast::Stmt::Template(t) => {
255 self.set_line_from_span(t.span());
256 for node in &t.children {
257 self.compile_stmt(node);
258 }
259 }
260 ast::Stmt::EmitExpr(expr) => {
261 self.compile_emit_expr(expr);
262 }
263 ast::Stmt::EmitRaw(raw) => {
264 self.set_line_from_span(raw.span());
265 self.add(Instruction::EmitRaw(raw.raw));
266 self.raw_template_bytes += raw.raw.len();
267 }
268 ast::Stmt::ForLoop(for_loop) => {
269 self.compile_for_loop(for_loop);
270 }
271 ast::Stmt::IfCond(if_cond) => {
272 self.compile_if_stmt(if_cond);
273 }
274 ast::Stmt::WithBlock(with_block) => {
275 self.set_line_from_span(with_block.span());
276 self.add(Instruction::PushWith);
277 for (target, expr) in &with_block.assignments {
278 self.compile_expr(expr);
279 self.compile_assignment(target);
280 }
281 for node in &with_block.body {
282 self.compile_stmt(node);
283 }
284 self.add(Instruction::PopFrame);
285 }
286 ast::Stmt::Set(set) => {
287 self.set_line_from_span(set.span());
288 self.compile_expr(&set.expr);
289 self.compile_assignment(&set.target);
290 }
291 ast::Stmt::SetBlock(set_block) => {
292 self.set_line_from_span(set_block.span());
293 self.add(Instruction::BeginCapture(CaptureMode::Capture));
294 for node in &set_block.body {
295 self.compile_stmt(node);
296 }
297 self.add(Instruction::EndCapture);
298 if let Some(ref filter) = set_block.filter {
299 self.compile_expr(filter);
300 }
301 self.compile_assignment(&set_block.target);
302 }
303 ast::Stmt::AutoEscape(auto_escape) => {
304 self.set_line_from_span(auto_escape.span());
305 self.compile_expr(&auto_escape.enabled);
306 self.add(Instruction::PushAutoEscape);
307 for node in &auto_escape.body {
308 self.compile_stmt(node);
309 }
310 self.add(Instruction::PopAutoEscape);
311 }
312 ast::Stmt::FilterBlock(filter_block) => {
313 self.set_line_from_span(filter_block.span());
314 self.add(Instruction::BeginCapture(CaptureMode::Capture));
315 for node in &filter_block.body {
316 self.compile_stmt(node);
317 }
318 self.add(Instruction::EndCapture);
319 self.compile_expr(&filter_block.filter);
320 self.add(Instruction::Emit);
321 }
322 #[cfg(feature = "multi_template")]
323 ast::Stmt::Block(block) => {
324 self.compile_block(block);
325 }
326 #[cfg(feature = "multi_template")]
327 ast::Stmt::Import(import) => {
328 self.add(Instruction::BeginCapture(CaptureMode::Capture));
329 self.add(Instruction::PushWith);
330 self.compile_expr(&import.expr);
331 self.add_with_span(Instruction::Include(false), import.span());
332 self.add(Instruction::EndCapture);
333 self.add(Instruction::ExportLocals);
334 self.add(Instruction::PopFrame);
335 self.compile_assignment(&import.name);
336 }
337 #[cfg(feature = "multi_template")]
338 ast::Stmt::FromImport(from_import) => {
339 self.add(Instruction::BeginCapture(CaptureMode::Discard));
340 self.add(Instruction::PushWith);
341 self.compile_expr(&from_import.expr);
342 self.add_with_span(Instruction::Include(false), from_import.span());
343 for (name, _) in &from_import.names {
344 self.compile_expr(name);
345 }
346 self.add(Instruction::PopFrame);
347 for (name, alias) in from_import.names.iter().rev() {
348 self.compile_assignment(alias.as_ref().unwrap_or(name));
349 }
350 self.add(Instruction::EndCapture);
351 }
352 #[cfg(feature = "multi_template")]
353 ast::Stmt::Extends(extends) => {
354 self.set_line_from_span(extends.span());
355 self.compile_expr(&extends.name);
356 self.add_with_span(Instruction::LoadBlocks, extends.span());
357 }
358 #[cfg(feature = "multi_template")]
359 ast::Stmt::Include(include) => {
360 self.set_line_from_span(include.span());
361 self.compile_expr(&include.name);
362 self.add_with_span(Instruction::Include(include.ignore_missing), include.span());
363 }
364 #[cfg(feature = "macros")]
365 ast::Stmt::Macro(macro_decl) => {
366 self.compile_macro(macro_decl);
367 }
368 #[cfg(feature = "macros")]
369 ast::Stmt::CallBlock(call_block) => {
370 self.compile_call_block(call_block);
371 }
372 #[cfg(feature = "loop_controls")]
373 ast::Stmt::Continue(cont) => {
374 self.set_line_from_span(cont.span());
375 for pending_block in self.pending_block.iter().rev() {
376 if let PendingBlock::Loop { iter_instr, .. } = pending_block {
377 self.add(Instruction::Jump(*iter_instr));
378 break;
379 }
380 }
381 }
382 #[cfg(feature = "loop_controls")]
383 ast::Stmt::Break(brk) => {
384 self.set_line_from_span(brk.span());
385 let instr = self.add(Instruction::Jump(0));
386 for pending_block in self.pending_block.iter_mut().rev() {
387 if let &mut PendingBlock::Loop {
388 ref mut jump_instrs,
389 ..
390 } = pending_block
391 {
392 jump_instrs.push(instr);
393 break;
394 }
395 }
396 }
397 ast::Stmt::Do(do_tag) => {
398 self.compile_do(do_tag);
399 }
400 }
401 }
402
403 #[cfg(feature = "multi_template")]
404 fn compile_block(&mut self, block: &ast::Spanned<ast::Block<'source>>) {
405 self.set_line_from_span(block.span());
406 let mut sub = self.new_subgenerator();
407 for node in &block.body {
408 sub.compile_stmt(node);
409 }
410 let instructions = self.finish_subgenerator(sub);
411 self.blocks.insert(block.name, instructions);
412 self.add(Instruction::CallBlock(block.name));
413 }
414
415 #[cfg(feature = "macros")]
416 fn compile_macro_expression(&mut self, macro_decl: &ast::Spanned<ast::Macro<'source>>) {
417 use crate::compiler::instructions::MACRO_CALLER;
418 self.set_line_from_span(macro_decl.span());
419 let instr = self.add(Instruction::Jump(!0));
420 let mut defaults_iter = macro_decl.defaults.iter().rev();
421 for arg in macro_decl.args.iter().rev() {
422 if let Some(default) = defaults_iter.next() {
423 self.add(Instruction::DupTop);
424 self.add(Instruction::IsUndefined);
425 self.start_if();
426 self.add(Instruction::DiscardTop);
427 self.compile_expr(default);
428 self.end_if();
429 }
430 self.compile_assignment(arg);
431 }
432 for node in ¯o_decl.body {
433 self.compile_stmt(node);
434 }
435 self.add(Instruction::Return);
436 let mut undeclared = crate::compiler::meta::find_macro_closure(macro_decl);
437 let caller_reference = undeclared.remove("caller");
438 let macro_instr = self.next_instruction();
439 for name in &undeclared {
440 self.add(Instruction::Enclose(name));
441 }
442 self.add(Instruction::GetClosure);
443 self.add(Instruction::LoadConst(Value::from_object(
444 macro_decl
445 .args
446 .iter()
447 .map(|x| match x {
448 ast::Expr::Var(var) => Value::from(var.id),
449 _ => unreachable!(),
450 })
451 .collect::<Vec<Value>>(),
452 )));
453 let mut flags = 0;
454 if caller_reference {
455 flags |= MACRO_CALLER;
456 }
457 self.add(Instruction::BuildMacro(macro_decl.name, instr + 1, flags));
458 if let Some(&mut Instruction::Jump(ref mut target)) = self.instructions.get_mut(instr) {
459 *target = macro_instr;
460 } else {
461 unreachable!();
462 }
463 }
464
465 #[cfg(feature = "macros")]
466 fn compile_macro(&mut self, macro_decl: &ast::Spanned<ast::Macro<'source>>) {
467 self.compile_macro_expression(macro_decl);
468 self.add(Instruction::StoreLocal(macro_decl.name));
469 }
470
471 #[cfg(feature = "macros")]
472 fn compile_call_block(&mut self, call_block: &ast::Spanned<ast::CallBlock<'source>>) {
473 self.compile_call(&call_block.call, Some(&call_block.macro_decl));
474 self.add(Instruction::Emit);
475 }
476
477 fn compile_do(&mut self, do_tag: &ast::Spanned<ast::Do<'source>>) {
478 self.compile_call(&do_tag.call, None);
479 }
480
481 fn compile_if_stmt(&mut self, if_cond: &ast::Spanned<ast::IfCond<'source>>) {
482 self.set_line_from_span(if_cond.span());
483 self.push_span(if_cond.expr.span());
484 self.compile_expr(&if_cond.expr);
485 self.start_if();
486 self.pop_span();
487 for node in &if_cond.true_body {
488 self.compile_stmt(node);
489 }
490 if !if_cond.false_body.is_empty() {
491 self.start_else();
492 for node in &if_cond.false_body {
493 self.compile_stmt(node);
494 }
495 }
496 self.end_if();
497 }
498
499 fn compile_emit_expr(&mut self, expr: &ast::Spanned<ast::EmitExpr<'source>>) {
500 if let ast::Expr::Call(call) = &expr.expr {
501 self.set_line_from_span(expr.expr.span());
502 match call.identify_call() {
503 ast::CallType::Function(name) => {
504 if name == "super" && call.args.is_empty() {
505 self.add_with_span(Instruction::FastSuper, call.span());
506 return;
507 } else if name == "loop" && call.args.len() == 1 {
508 self.compile_call_args(std::slice::from_ref(&call.args[0]), 0, None);
509 self.add_with_span(Instruction::FastRecurse, call.span());
510 return;
511 }
512 }
513 #[cfg(feature = "multi_template")]
514 ast::CallType::Block(name) => {
515 self.add(Instruction::CallBlock(name));
516 return;
517 }
518 _ => {}
519 }
520 }
521 self.push_span(expr.expr.span());
522 self.compile_expr(&expr.expr);
523 self.add(Instruction::Emit);
524 self.pop_span();
525 }
526
527 fn compile_for_loop(&mut self, for_loop: &ast::Spanned<ast::ForLoop<'source>>) {
528 self.set_line_from_span(for_loop.span());
529
530 if let Some(ref filter_expr) = for_loop.filter_expr {
535 self.add(Instruction::LoadConst(Value::from(0usize)));
536 self.push_span(filter_expr.span());
537 self.compile_expr(&for_loop.iter);
538 self.start_for_loop(false, false);
539 self.add(Instruction::DupTop);
540 self.compile_assignment(&for_loop.target);
541 self.compile_expr(filter_expr);
542 self.start_if();
543 self.add(Instruction::Swap);
544 self.add(Instruction::LoadConst(Value::from(1usize)));
545 self.add(Instruction::Add);
546 self.start_else();
547 self.add(Instruction::DiscardTop);
548 self.end_if();
549 self.pop_span();
550 self.end_for_loop(false);
551 self.add(Instruction::BuildList(None));
552 self.start_for_loop(true, for_loop.recursive);
553 } else {
554 self.push_span(for_loop.iter.span());
555 self.compile_expr(&for_loop.iter);
556 self.start_for_loop(true, for_loop.recursive);
557 self.pop_span();
558 }
559
560 self.compile_assignment(&for_loop.target);
561 for node in &for_loop.body {
562 self.compile_stmt(node);
563 }
564 self.end_for_loop(!for_loop.else_body.is_empty());
565 if !for_loop.else_body.is_empty() {
566 self.start_if();
567 for node in &for_loop.else_body {
568 self.compile_stmt(node);
569 }
570 self.end_if();
571 };
572 }
573
574 pub fn compile_assignment(&mut self, expr: &ast::Expr<'source>) {
576 match expr {
577 ast::Expr::Var(var) => {
578 self.add(Instruction::StoreLocal(var.id));
579 }
580 ast::Expr::List(list) => {
581 self.push_span(list.span());
582 self.add(Instruction::UnpackList(list.items.len()));
583 for expr in &list.items {
584 self.compile_assignment(expr);
585 }
586 self.pop_span();
587 }
588 ast::Expr::GetAttr(attr) => {
589 self.push_span(attr.span());
590 self.compile_expr(&attr.expr);
591 self.add(Instruction::SetAttr(attr.name));
592 }
593 _ => unreachable!(),
594 }
595 }
596
597 pub fn compile_expr(&mut self, expr: &ast::Expr<'source>) {
599 if let Some(v) = expr.as_const() {
601 self.set_line_from_span(expr.span());
602 self.add(Instruction::LoadConst(v.clone()));
603 return;
604 }
605
606 match expr {
607 ast::Expr::Var(v) => {
608 self.set_line_from_span(v.span());
609 self.add(Instruction::Lookup(v.id));
610 }
611 ast::Expr::Const(_) => unreachable!(), ast::Expr::Slice(s) => {
613 self.push_span(s.span());
614 self.compile_expr(&s.expr);
615 if let Some(ref start) = s.start {
616 self.compile_expr(start);
617 } else {
618 self.add(Instruction::LoadConst(Value::from(())));
619 }
620 if let Some(ref stop) = s.stop {
621 self.compile_expr(stop);
622 } else {
623 self.add(Instruction::LoadConst(Value::from(())));
624 }
625 if let Some(ref step) = s.step {
626 self.compile_expr(step);
627 } else {
628 self.add(Instruction::LoadConst(Value::from(())));
629 }
630 self.add(Instruction::Slice);
631 self.pop_span();
632 }
633 ast::Expr::UnaryOp(c) => {
634 self.set_line_from_span(c.span());
635 match c.op {
636 ast::UnaryOpKind::Not => {
637 self.compile_expr(&c.expr);
638 self.add(Instruction::Not);
639 }
640 ast::UnaryOpKind::Neg => {
641 if let ast::Expr::Const(ref c) = c.expr {
645 if let Ok(negated) = neg(&c.value) {
646 self.add(Instruction::LoadConst(negated));
647 return;
648 }
649 }
650 self.compile_expr(&c.expr);
651 self.add_with_span(Instruction::Neg, c.span());
652 }
653 }
654 }
655 ast::Expr::BinOp(c) => {
656 self.compile_bin_op(c);
657 }
658 ast::Expr::IfExpr(i) => {
659 self.set_line_from_span(i.span());
660 self.compile_expr(&i.test_expr);
661 self.start_if();
662 self.compile_expr(&i.true_expr);
663 self.start_else();
664 if let Some(ref false_expr) = i.false_expr {
665 self.compile_expr(false_expr);
666 } else {
667 self.add(Instruction::LoadConst(
671 ValueRepr::Undefined(UndefinedType::Silent).into(),
672 ));
673 }
674 self.end_if();
675 }
676 ast::Expr::Filter(f) => {
677 self.push_span(f.span());
678 if let Some(ref expr) = f.expr {
679 self.compile_expr(expr);
680 }
681 let arg_count = self.compile_call_args(&f.args, 1, None);
682 let local_id = get_local_id(&mut self.filter_local_ids, f.name);
683 self.add(Instruction::ApplyFilter(f.name, arg_count, local_id));
684 self.pop_span();
685 }
686 ast::Expr::Test(f) => {
687 self.push_span(f.span());
688 self.compile_expr(&f.expr);
689 let arg_count = self.compile_call_args(&f.args, 1, None);
690 let local_id = get_local_id(&mut self.test_local_ids, f.name);
691 self.add(Instruction::PerformTest(f.name, arg_count, local_id));
692 self.pop_span();
693 }
694 ast::Expr::GetAttr(g) => {
695 self.push_span(g.span());
696 self.compile_expr(&g.expr);
697 self.add(Instruction::GetAttr(g.name));
698 self.pop_span();
699 }
700 ast::Expr::GetItem(g) => {
701 self.push_span(g.span());
702 self.compile_expr(&g.expr);
703 self.compile_expr(&g.subscript_expr);
704 self.add(Instruction::GetItem);
705 self.pop_span();
706 }
707 ast::Expr::Call(c) => {
708 self.compile_call(c, None);
709 }
710 ast::Expr::List(l) => {
711 self.set_line_from_span(l.span());
712 for item in &l.items {
713 self.compile_expr(item);
714 }
715 self.add(Instruction::BuildList(Some(l.items.len())));
716 }
717 ast::Expr::Map(m) => {
718 self.set_line_from_span(m.span());
719 assert_eq!(m.keys.len(), m.values.len());
720 for (key, value) in m.keys.iter().zip(m.values.iter()) {
721 self.compile_expr(key);
722 self.compile_expr(value);
723 }
724 self.add(Instruction::BuildMap(m.keys.len()));
725 }
726 }
727 }
728
729 fn compile_call(
730 &mut self,
731 c: &ast::Spanned<ast::Call<'source>>,
732 caller: Option<&Caller<'source>>,
733 ) {
734 self.push_span(c.span());
735 match c.identify_call() {
736 ast::CallType::Function(name) => {
737 let arg_count = self.compile_call_args(&c.args, 0, caller);
738 self.add(Instruction::CallFunction(name, arg_count));
739 }
740 #[cfg(feature = "multi_template")]
741 ast::CallType::Block(name) => {
742 self.add(Instruction::BeginCapture(CaptureMode::Capture));
743 self.add(Instruction::CallBlock(name));
744 self.add(Instruction::EndCapture);
745 }
746 ast::CallType::Method(expr, name) => {
747 self.compile_expr(expr);
748 let arg_count = self.compile_call_args(&c.args, 1, caller);
749 self.add(Instruction::CallMethod(name, arg_count));
750 }
751 ast::CallType::Object(expr) => {
752 self.compile_expr(expr);
753 let arg_count = self.compile_call_args(&c.args, 1, caller);
754 self.add(Instruction::CallObject(arg_count));
755 }
756 };
757 self.pop_span();
758 }
759
760 fn compile_call_args(
761 &mut self,
762 args: &[ast::CallArg<'source>],
763 extra_args: usize,
764 caller: Option<&Caller<'source>>,
765 ) -> Option<u16> {
766 let mut pending_args = extra_args;
767 let mut num_args_batches = 0;
768 let mut has_kwargs = caller.is_some();
769 let mut static_kwargs = caller.is_none();
770
771 for arg in args {
772 match arg {
773 ast::CallArg::Pos(expr) => {
774 self.compile_expr(expr);
775 pending_args += 1;
776 }
777 ast::CallArg::PosSplat(expr) => {
778 if pending_args > 0 {
779 self.add(Instruction::BuildList(Some(pending_args)));
780 pending_args = 0;
781 num_args_batches += 1;
782 }
783 self.compile_expr(expr);
784 num_args_batches += 1;
785 }
786 ast::CallArg::Kwarg(_, expr) => {
787 if !matches!(expr, ast::Expr::Const(_)) {
788 static_kwargs = false;
789 }
790 has_kwargs = true;
791 }
792 ast::CallArg::KwargSplat(_) => {
793 static_kwargs = false;
794 has_kwargs = true;
795 }
796 }
797 }
798
799 if has_kwargs {
800 let mut pending_kwargs = 0;
801 let mut num_kwargs_batches = 0;
802 let mut collected_kwargs = ValueMap::new();
803 for arg in args {
804 match arg {
805 ast::CallArg::Kwarg(key, value) => {
806 if static_kwargs {
807 if let ast::Expr::Const(c) = value {
808 collected_kwargs.insert(Value::from(*key), c.value.clone());
809 } else {
810 unreachable!();
811 }
812 } else {
813 self.add(Instruction::LoadConst(Value::from(*key)));
814 self.compile_expr(value);
815 pending_kwargs += 1;
816 }
817 }
818 ast::CallArg::KwargSplat(expr) => {
819 if pending_kwargs > 0 {
820 self.add(Instruction::BuildKwargs(pending_kwargs));
821 num_kwargs_batches += 1;
822 pending_kwargs = 0;
823 }
824 self.compile_expr(expr);
825 num_kwargs_batches += 1;
826 }
827 ast::CallArg::Pos(_) | ast::CallArg::PosSplat(_) => {}
828 }
829 }
830
831 if !collected_kwargs.is_empty() {
832 self.add(Instruction::LoadConst(Kwargs::wrap(collected_kwargs)));
833 } else {
834 #[cfg(feature = "macros")]
838 {
839 if let Some(caller) = caller {
840 self.add(Instruction::LoadConst(Value::from("caller")));
841 self.compile_macro_expression(caller);
842 pending_kwargs += 1
843 }
844 }
845 if num_kwargs_batches > 0 {
846 if pending_kwargs > 0 {
847 self.add(Instruction::BuildKwargs(pending_kwargs));
848 num_kwargs_batches += 1;
849 }
850 self.add(Instruction::MergeKwargs(num_kwargs_batches));
851 } else {
852 self.add(Instruction::BuildKwargs(pending_kwargs));
853 }
854 }
855 pending_args += 1;
856 }
857
858 if num_args_batches > 0 {
859 if pending_args > 0 {
860 self.add(Instruction::BuildList(Some(pending_args)));
861 num_args_batches += 1;
862 }
863 self.add(Instruction::UnpackLists(num_args_batches));
864 None
865 } else {
866 assert!(pending_args as u16 as usize == pending_args);
867 Some(pending_args as u16)
868 }
869 }
870
871 fn compile_bin_op(&mut self, c: &ast::Spanned<ast::BinOp<'source>>) {
872 self.push_span(c.span());
873 let instr = match c.op {
874 ast::BinOpKind::Eq => Instruction::Eq,
875 ast::BinOpKind::Ne => Instruction::Ne,
876 ast::BinOpKind::Lt => Instruction::Lt,
877 ast::BinOpKind::Lte => Instruction::Lte,
878 ast::BinOpKind::Gt => Instruction::Gt,
879 ast::BinOpKind::Gte => Instruction::Gte,
880 ast::BinOpKind::ScAnd | ast::BinOpKind::ScOr => {
881 self.start_sc_bool();
882 self.compile_expr(&c.left);
883 self.sc_bool(matches!(c.op, ast::BinOpKind::ScAnd));
884 self.compile_expr(&c.right);
885 self.end_sc_bool();
886 self.pop_span();
887 return;
888 }
889 ast::BinOpKind::Add => Instruction::Add,
890 ast::BinOpKind::Sub => Instruction::Sub,
891 ast::BinOpKind::Mul => Instruction::Mul,
892 ast::BinOpKind::Div => Instruction::Div,
893 ast::BinOpKind::FloorDiv => Instruction::IntDiv,
894 ast::BinOpKind::Rem => Instruction::Rem,
895 ast::BinOpKind::Pow => Instruction::Pow,
896 ast::BinOpKind::Concat => Instruction::StringConcat,
897 ast::BinOpKind::In => Instruction::In,
898 };
899 self.compile_expr(&c.left);
900 self.compile_expr(&c.right);
901 self.add(instr);
902 self.pop_span();
903 }
904
905 pub fn buffer_size_hint(&self) -> usize {
909 (self.raw_template_bytes * 2).next_power_of_two()
914 }
915
916 pub fn finish(
918 self,
919 ) -> (
920 Instructions<'source>,
921 BTreeMap<&'source str, Instructions<'source>>,
922 ) {
923 assert!(self.pending_block.is_empty());
924 (self.instructions, self.blocks)
925 }
926}