round-3-gamma: combine c_escape + scan_interp_string batching — max round-3 savings

Combines two orthogonal optimizations:
1. c_escape batching (from alpha): ASCII runs emitted as str_slice segments instead
   of one str_char_at string per byte. O(N) allocs → O(K) where K = special chars.

2. scan_interp_string batching (from beta): char dispatch via str_char_code (Int)
   + clean_start tracking to flush plain runs as str_slice. Eliminates per-char
   string allocations in the string-literal scanning hot path.

Result on web/src/main.el: 14.5MB -> 13.4MB peak RSS (-7.6%).
Self-hosting: PASS.
This commit is contained in:
Will Anderson
2026-05-05 16:01:05 -05:00
parent 1eef9928f4
commit e587bedf30
2 changed files with 117 additions and 46 deletions
+69 -34
View File
@@ -555,10 +555,17 @@ fn interp_tokens_append_all(dst: [Any], src: [Any]) -> [Any] {
//
// Supported escape sequences: \" \n \t \r \\ \$ (literal dollar sign).
// Nested quotes inside ${} are not supported; use a variable instead.
//
// Performance: uses str_char_code (Int) for all character dispatch, eliminating
// per-character strdup. Plain runs are batched into str_slice segments instead
// of accumulating single-char strings, reducing list appends from O(N) to O(K)
// where K = number of escape/special chars in the literal.
// Char codes: '\' = 92, '"' = 34, '$' = 36, '{' = 123
fn scan_interp_string(src: String, start: Int, total: Int) -> Map<String, Any> {
let i = start
let out_tokens: [Any] = native_list_empty()
let cur_part: [String] = native_list_empty()
let cur_parts: [String] = native_list_empty()
let clean_start = start
let has_interp = false
let need_plus = false
let running = true
@@ -567,39 +574,55 @@ fn scan_interp_string(src: String, start: Int, total: Int) -> Map<String, Any> {
if i >= total {
let running = false
} else {
let ch: String = str_char_at(src, i)
let c: Int = str_char_code(src, i)
if ch == "\\" {
// Escape sequence
if c == 92 {
// '\\' = 92 escape sequence: flush clean run, append resolved char
if clean_start < i {
let cur_parts = native_list_append(cur_parts, str_slice(src, clean_start, i))
}
let next_i = i + 1
if next_i < total {
let next_ch: String = str_char_at(src, next_i)
if next_ch == "$" {
// \$ => literal '$' (escape for interpolation syntax)
let cur_part = native_list_append(cur_part, "$")
let nc: Int = str_char_code(src, next_i)
if nc == 36 {
// '\$' => literal '$' (36 = '$')
let cur_parts = native_list_append(cur_parts, "$")
let clean_start = next_i + 1
let i = next_i + 1
} else {
if next_ch == "\"" {
let cur_part = native_list_append(cur_part, "\"")
if nc == 34 {
// '\"' => literal '"' (34 = '"')
let cur_parts = native_list_append(cur_parts, "\"")
let clean_start = next_i + 1
let i = next_i + 1
} else {
if next_ch == "n" {
let cur_part = native_list_append(cur_part, "\n")
if nc == 110 {
// '\n' (110 = 'n')
let cur_parts = native_list_append(cur_parts, "\n")
let clean_start = next_i + 1
let i = next_i + 1
} else {
if next_ch == "t" {
let cur_part = native_list_append(cur_part, "\t")
if nc == 116 {
// '\t' (116 = 't')
let cur_parts = native_list_append(cur_parts, "\t")
let clean_start = next_i + 1
let i = next_i + 1
} else {
if next_ch == "r" {
let cur_part = native_list_append(cur_part, "\r")
if nc == 114 {
// '\r' (114 = 'r')
let cur_parts = native_list_append(cur_parts, "\r")
let clean_start = next_i + 1
let i = next_i + 1
} else {
if next_ch == "\\" {
let cur_part = native_list_append(cur_part, "\\")
if nc == 92 {
// '\\' (92)
let cur_parts = native_list_append(cur_parts, "\\")
let clean_start = next_i + 1
let i = next_i + 1
} else {
let cur_part = native_list_append(cur_part, next_ch)
// Unknown escape: emit the escaped char verbatim
let cur_parts = native_list_append(cur_parts, str_slice(src, next_i, next_i + 1))
let clean_start = next_i + 1
let i = next_i + 1
}
}
@@ -608,29 +631,38 @@ fn scan_interp_string(src: String, start: Int, total: Int) -> Map<String, Any> {
}
}
} else {
let i = i + 1
let clean_start = next_i
let i = next_i
}
} else {
if ch == "\"" {
// Closing quote - stop scanning
if c == 34 {
// '"' = 34 — closing quote: flush clean run, stop
if clean_start < i {
let cur_parts = native_list_append(cur_parts, str_slice(src, clean_start, i))
}
let i = i + 1
let clean_start = i
let running = false
} else {
if ch == "$" {
// Check for ${ (start of interpolation)
if c == 36 {
// '$' = 36 — possible interpolation start
let next_i = i + 1
let is_interp = false
if next_i < total {
let next_ch: String = str_char_at(src, next_i)
if next_ch == "{" {
let nc2: Int = str_char_code(src, next_i)
if nc2 == 123 {
// '{' = 123
let is_interp = true
}
}
if is_interp {
// Flush the accumulated literal part (if non-empty)
let part_len: Int = native_list_len(cur_part)
if clean_start < i {
let cur_parts = native_list_append(cur_parts, str_slice(src, clean_start, i))
}
let part_len: Int = native_list_len(cur_parts)
if part_len > 0 {
let part_text = str_join(cur_part, "")
let part_text = str_join(cur_parts, "")
if need_plus {
let out_tokens = tok_append(out_tokens, "Plus", "+")
}
@@ -641,7 +673,7 @@ fn scan_interp_string(src: String, start: Int, total: Int) -> Map<String, Any> {
let out_tokens = tok_append(out_tokens, "Str", clean_part)
let need_plus = true
}
let cur_part = native_list_empty()
let cur_parts = native_list_empty()
let has_interp = true
// Scan brace-balanced expression source
@@ -649,6 +681,7 @@ fn scan_interp_string(src: String, start: Int, total: Int) -> Map<String, Any> {
let expr_src: String = brace_result["text"]
let new_i: Int = brace_result["pos"]
let i = new_i
let clean_start = new_i
// Re-lex the expression and inline the tokens.
// Wrap in ( ) so that operators inside ${} (e.g.
@@ -672,12 +705,11 @@ fn scan_interp_string(src: String, start: Int, total: Int) -> Map<String, Any> {
}
let need_plus = true
} else {
// Plain '$' not followed by '{' - treat as literal
let cur_part = native_list_append(cur_part, "$")
// Plain '$' not followed by '{' - treat as literal, continue clean run
let i = i + 1
}
} else {
let cur_part = native_list_append(cur_part, ch)
// Plain char — extends clean run, no append needed
let i = i + 1
}
}
@@ -686,8 +718,11 @@ fn scan_interp_string(src: String, start: Int, total: Int) -> Map<String, Any> {
}
// Flush remaining literal segment and build final token list
let part_text = str_join(cur_part, "")
let part_len: Int = native_list_len(cur_part)
if clean_start < i {
let cur_parts = native_list_append(cur_parts, str_slice(src, clean_start, i))
}
let part_len: Int = native_list_len(cur_parts)
let part_text = str_join(cur_parts, "")
if has_interp {
// Interpolated string: only emit trailing segment if non-empty
if part_len > 0 {