|
1 | 1 | use std::borrow::Cow;
|
2 | 2 | use std::collections::hash_map::{Entry, HashMap};
|
3 |
| -use std::fmt::{self, Write}; |
| 3 | +use std::fmt::{self, Debug, Write}; |
4 | 4 | use std::mem;
|
5 | 5 |
|
6 | 6 | use parser::node::{
|
@@ -96,7 +96,7 @@ impl<'a> Generator<'a, '_> {
|
96 | 96 | self.write_comment(comment);
|
97 | 97 | }
|
98 | 98 | Node::Expr(ws, ref val) => {
|
99 |
| - self.write_expr(ws, val); |
| 99 | + size_hint += self.write_expr(ctx, buf, ws, val)?; |
100 | 100 | }
|
101 | 101 | Node::Let(ref l) => {
|
102 | 102 | self.write_let(ctx, buf, l)?;
|
@@ -600,18 +600,10 @@ impl<'a> Generator<'a, '_> {
|
600 | 600 | scope,
|
601 | 601 | name,
|
602 | 602 | ref args,
|
603 |
| - ref block, |
| 603 | + .. |
604 | 604 | } = **call;
|
605 |
| - if name == "super" { |
606 |
| - return self.write_block(ctx, buf, None, ws, call.span()); |
607 |
| - } |
608 | 605 |
|
609 |
| - let (def, own_ctx) = if name == "caller" { |
610 |
| - let def = self.seen_call_macro.last().ok_or_else(|| { |
611 |
| - ctx.generate_error(format_args!("block is not defined for caller"), call.span()) |
612 |
| - })?; |
613 |
| - (*def, ctx) |
614 |
| - } else if let Some(s) = scope { |
| 606 | + let (def, own_ctx) = if let Some(s) = scope { |
615 | 607 | let path = ctx.imports.get(s).ok_or_else(|| {
|
616 | 608 | ctx.generate_error(format_args!("no import found for scope {s:?}"), call.span())
|
617 | 609 | })?;
|
@@ -645,9 +637,7 @@ impl<'a> Generator<'a, '_> {
|
645 | 637 | } else {
|
646 | 638 | self.seen_macros.push((def, ctx.file_info_of(call.span())));
|
647 | 639 | }
|
648 |
| - if let Some(macr) = block { |
649 |
| - self.seen_call_macro.push(macr); |
650 |
| - } |
| 640 | + self.seen_callers.push(call); |
651 | 641 | self.flush_ws(ws); // Cannot handle_ws() here: whitespace from macro definition comes first
|
652 | 642 | let size_hint = self.push_locals(|this| {
|
653 | 643 | macro_call_ensure_arg_count(call, def, ctx)?;
|
@@ -771,9 +761,7 @@ impl<'a> Generator<'a, '_> {
|
771 | 761 | })?;
|
772 | 762 | self.prepare_ws(ws);
|
773 | 763 | self.seen_macros.pop();
|
774 |
| - if let Some(_macr) = block { |
775 |
| - self.seen_call_macro.pop(); |
776 |
| - } |
| 764 | + self.seen_callers.pop(); |
777 | 765 | Ok(size_hint)
|
778 | 766 | }
|
779 | 767 |
|
@@ -1103,17 +1091,108 @@ impl<'a> Generator<'a, '_> {
|
1103 | 1091 | Ok(size_hint)
|
1104 | 1092 | }
|
1105 | 1093 |
|
1106 |
| - fn write_expr(&mut self, ws: Ws, s: &'a WithSpan<'a, Expr<'a>>) { |
| 1094 | + fn write_expr( |
| 1095 | + &mut self, |
| 1096 | + ctx: &Context<'a>, |
| 1097 | + buf: &mut Buffer, |
| 1098 | + ws: Ws, |
| 1099 | + s: &'a WithSpan<'a, Expr<'a>>, |
| 1100 | + ) -> Result<usize, CompileError> { |
| 1101 | + if let Expr::Call { |
| 1102 | + ref path, |
| 1103 | + args: expr_args, |
| 1104 | + .. |
| 1105 | + } = &**s |
| 1106 | + { |
| 1107 | + if ***path == Expr::Var("super") { |
| 1108 | + return self.write_block(ctx, buf, None, ws, s.span()); |
| 1109 | + } else if ***path == Expr::Var("caller") { |
| 1110 | + let def = self.seen_callers.last().ok_or_else(|| { |
| 1111 | + ctx.generate_error(format_args!("block is not defined for caller"), s.span()) |
| 1112 | + })?; |
| 1113 | + //self.flush_ws(ws); // Cannot handle_ws() here: whitespace from macro definition comes first |
| 1114 | + let size_hint = self.push_locals(|this| { |
| 1115 | + this.write_buf_writable(ctx, buf)?; |
| 1116 | + buf.write('{'); |
| 1117 | + this.prepare_ws(def.ws1); |
| 1118 | + let mut value = Buffer::new(); |
| 1119 | + for (index, arg) in def.caller_args.iter().enumerate() { |
| 1120 | + match expr_args.get(index) { |
| 1121 | + Some(expr) => { |
| 1122 | + match &**expr { |
| 1123 | + // If `expr` is already a form of variable then |
| 1124 | + // don't reintroduce a new variable. This is |
| 1125 | + // to avoid moving non-copyable values. |
| 1126 | + Expr::Var(name) if *name != "self" => { |
| 1127 | + let var = this.locals.resolve_or_self(name); |
| 1128 | + this.locals |
| 1129 | + .insert(Cow::Borrowed(arg), LocalMeta::with_ref(var)); |
| 1130 | + } |
| 1131 | + Expr::Attr(obj, attr) => { |
| 1132 | + let mut attr_buf = Buffer::new(); |
| 1133 | + this.visit_attr(ctx, &mut attr_buf, obj, attr)?; |
| 1134 | + |
| 1135 | + let attr = attr_buf.into_string(); |
| 1136 | + let var = this.locals.resolve(&attr).unwrap_or(attr); |
| 1137 | + this.locals |
| 1138 | + .insert(Cow::Borrowed(arg), LocalMeta::with_ref(var)); |
| 1139 | + } |
| 1140 | + // Everything else still needs to become variables, |
| 1141 | + // to avoid having the same logic be executed |
| 1142 | + // multiple times, e.g. in the case of macro |
| 1143 | + // parameters being used multiple times. |
| 1144 | + _ => { |
| 1145 | + value.clear(); |
| 1146 | + let (before, after) = if !is_copyable(expr) { |
| 1147 | + ("&(", ")") |
| 1148 | + } else { |
| 1149 | + ("", "") |
| 1150 | + }; |
| 1151 | + value.write(this.visit_expr_root(ctx, expr)?); |
| 1152 | + // We need to normalize the arg to write it, thus we need to add it to |
| 1153 | + // locals in the normalized manner |
| 1154 | + let normalized_arg = normalize_identifier(arg); |
| 1155 | + buf.write(format_args!( |
| 1156 | + "let {} = {before}{value}{after};", |
| 1157 | + normalized_arg |
| 1158 | + )); |
| 1159 | + this.locals |
| 1160 | + .insert_with_default(Cow::Borrowed(normalized_arg)); |
| 1161 | + } |
| 1162 | + } |
| 1163 | + } |
| 1164 | + None => { |
| 1165 | + return Err(ctx.generate_error( |
| 1166 | + format_args!("missing `{arg}` argument"), |
| 1167 | + s.span(), |
| 1168 | + )); |
| 1169 | + } |
| 1170 | + } |
| 1171 | + } |
| 1172 | + |
| 1173 | + let mut size_hint = this.handle(ctx, &def.nodes, buf, AstLevel::Nested)?; |
| 1174 | + this.flush_ws(def.ws2); |
| 1175 | + size_hint += this.write_buf_writable(ctx, buf)?; |
| 1176 | + buf.write('}'); |
| 1177 | + Ok(size_hint) |
| 1178 | + })?; |
| 1179 | + self.prepare_ws(ws); |
| 1180 | + return Ok(size_hint); |
| 1181 | + } |
| 1182 | + } |
| 1183 | + |
1107 | 1184 | self.handle_ws(ws);
|
1108 | 1185 | let items = if let Expr::Concat(exprs) = &**s {
|
1109 | 1186 | exprs
|
1110 | 1187 | } else {
|
1111 | 1188 | std::slice::from_ref(s)
|
1112 | 1189 | };
|
| 1190 | + |
1113 | 1191 | for s in items {
|
1114 | 1192 | self.buf_writable
|
1115 | 1193 | .push(compile_time_escape(s, self.input.escaper).unwrap_or(Writable::Expr(s)));
|
1116 | 1194 | }
|
| 1195 | + Ok(0) |
1117 | 1196 | }
|
1118 | 1197 |
|
1119 | 1198 | // Write expression buffer and empty
|
|
0 commit comments