MCPcopy Create free account
hub / github.com/dylan-sutton-chavez/edge-python / fstring

Method fstring

compiler/src/modules/parser/literals.rs:233–353  ·  view source on GitHub ↗

f-string: parses literal+expr chunks until FstringEnd; `fs_start/fs_end` anchor unclosed-string errors. */

(&mut self, fs_start: usize, fs_end: usize)

Source from the content-addressed store, hash-verified

231
232 /* f-string: parses literal+expr chunks until FstringEnd; `fs_start/fs_end` anchor unclosed-string errors. */
233 pub(super) fn fstring(&mut self, fs_start: usize, fs_end: usize) {
234 let mut parts = 0u16;
235 let mut got_end = false;
236 // Raw f-strings (`rf"..."`) keep backslashes literal; plain ones decode escapes like a normal string.
237 let is_raw = super::types::has_raw_prefix(&self.source[fs_start..fs_end]);
238 if matches!(self.peek(), Some(TokenType::FstringEnd)) {
239 self.advance();
240 self.emit_const(Value::Str(String::new()));
241 return;
242 }
243 loop {
244 match self.peek() {
245 Some(TokenType::FstringMiddle) => {
246 let t = self.advance();
247 let raw = self.lexeme(&t);
248 let mut unescaped = String::with_capacity(raw.len());
249 // Single pass so `{{` is seen in the raw text before any escape can produce a brace.
250 let mut chars = raw.chars().peekable();
251 while let Some(c) = chars.next() {
252 match c {
253 '{' if chars.peek() == Some(&'{') => { chars.next(); unescaped.push('{'); }
254 '}' if chars.peek() == Some(&'}') => { chars.next(); unescaped.push('}'); }
255 '\\' if !is_raw => super::types::push_escape(&mut unescaped, &mut chars),
256 _ => unescaped.push(c),
257 }
258 }
259 self.emit_const(Value::Str(unescaped));
260 parts += 1;
261 }
262 Some(TokenType::Lbrace) => {
263 self.advance();
264 // Capture span for `f"{expr=}"` debug prefix.
265 let expr_start_byte = self.tokens.peek().map(|t| t.start).unwrap_or(0);
266 let insn_start = self.chunk.instructions.len();
267 let saved_in_fstring = self.in_fstring_expr;
268 self.in_fstring_expr = true;
269 self.expr();
270 // Bare tuple in a replacement field: `f"{1,}"` builds (1,).
271 if matches!(self.peek(), Some(TokenType::Comma)) {
272 let mut n = 1u16;
273 while self.eat_if(TokenType::Comma) {
274 if matches!(self.peek(), Some(TokenType::Rbrace | TokenType::Colon | TokenType::Exclamation | TokenType::Equal) | None) { break; }
275 self.expr();
276 n += 1;
277 }
278 self.chunk.emit(OpCode::BuildTuple, n);
279 }
280 self.in_fstring_expr = saved_in_fstring;
281 let expr_end_byte = self.last_end;
282 /* FormatValue operand: bit0=has-spec, bits1-2=conversion (0=none,1=!r,2=!s,3=!a). */
283 let mut flags = 0u16;
284 // `=` debug: emits "expr=" prefix; defaults to !r when no conv/spec given.
285 let mut debug_prefix: Option<String> = None;
286 if matches!(self.peek(), Some(TokenType::Equal)) {
287 self.advance();
288 let raw = &self.source[expr_start_byte..expr_end_byte];
289 debug_prefix = Some(s!(str raw, "="));
290 }

Callers 1

parse_atomMethod · 0.80

Calls 15

has_raw_prefixFunction · 0.85
push_escapeFunction · 0.85
advanceMethod · 0.80
emit_constMethod · 0.80
lexemeMethod · 0.80
nextMethod · 0.80
pushMethod · 0.80
exprMethod · 0.80
eat_ifMethod · 0.80
emitMethod · 0.80
error_atMethod · 0.80
takeMethod · 0.80

Tested by

no test coverage detected