///| Incremental parsing benchmarks (split from bench.mbt).
// === Incremental parsing benchmarks ===
// Generate a document with many paragraphs for incremental testing
///|
fn generate_paragraph_doc(count : Int) -> String {
let buf = StringBuilder::new()
buf.write_string("# Document Title\n\n")
for i = 0; i < count; i = i + 1 {
buf.write_string(
"Paragraph \{i + 1} with some content that needs parsing. ",
)
buf.write_string("This is a *sample* paragraph with **formatting**.\n\n")
}
buf.to_string()
}
///|
let incremental_doc_10 : String = generate_paragraph_doc(10)
///|
let incremental_doc_50 : String = generate_paragraph_doc(50)
///|
let incremental_doc_100 : String = generate_paragraph_doc(100)
// Pre-parse documents for incremental benchmarks
///|
let incremental_parsed_10 : ParseResult = parse(incremental_doc_10)
///|
let incremental_parsed_50 : ParseResult = parse(incremental_doc_50)
///|
let incremental_parsed_100 : ParseResult = parse(incremental_doc_100)
// Pre-compute edited versions for benchmarks (avoid computing in bench loop)
///|
let edited_10 : (String, EditInfo) = edit_middle_paragraph(incremental_doc_10)
///|
let edited_50 : (String, EditInfo) = edit_middle_paragraph(incremental_doc_50)
///|
let edited_100 : (String, EditInfo) = edit_middle_paragraph(incremental_doc_100)
// Pre-compute edge case edits
///|
let edited_50_start : (String, EditInfo) = {
let new_source = "# CHANGED Title\n" +
incremental_doc_50.unsafe_substring(
start=18,
end=incremental_doc_50.length(),
)
let edit = EditInfo::replace(2, 8, 14)
(new_source, edit)
}
///|
let edited_50_end : (String, EditInfo) = {
let new_source = incremental_doc_50 + "New final paragraph added here.\n\n"
let edit = EditInfo::insert(incremental_doc_50.length(), 34)
(new_source, edit)
}
// Helper to create edited version (change middle paragraph)
///|
fn edit_middle_paragraph(source : String) -> (String, EditInfo) {
// Find approximately middle position
let mid = source.length() / 2
// Find start of a line near middle
let mut edit_pos = mid
for i = mid; i > 0; i = i - 1 {
match source.get_char(i) {
Some('\n') => {
edit_pos = i + 1
break
}
_ => continue
}
}
// Replace "Paragraph" with "CHANGED" at that position
let search = "Paragraph"
let replace = "PARAGRAPH"
let edit_offset = match source.find(search) {
Some(pos) => pos + edit_pos / 10 // Offset to get middle occurrence
None => edit_pos
}
let new_source = source.unsafe_substring(start=0, end=edit_offset) +
replace +
source.unsafe_substring(
start=edit_offset + search.length(),
end=source.length(),
)
let edit = EditInfo::replace(edit_offset, search.length(), replace.length())
(new_source, edit)
}
// Baseline: Full re-parse of 10-paragraph document
///|
test "full parse: 10 paragraphs" (b : @bench.T) {
b.bench(fn() {
let result = parse(incremental_doc_10)
b.keep(result)
})
}
// Incremental: Edit middle of 10-paragraph document
///|
test "incremental: 10 paragraphs, edit middle" (b : @bench.T) {
let (new_source, edit) = edited_10
let old_doc = incremental_parsed_10.document
b.bench(fn() {
let result = parse_incremental(
old_doc, incremental_doc_10, new_source, edit,
)
b.keep(result)
})
}
// Baseline: Full re-parse of 50-paragraph document
///|
test "full parse: 50 paragraphs" (b : @bench.T) {
b.bench(fn() {
let result = parse(incremental_doc_50)
b.keep(result)
})
}
// Incremental: Edit middle of 50-paragraph document
///|
test "incremental: 50 paragraphs, edit middle" (b : @bench.T) {
let (new_source, edit) = edited_50
let old_doc = incremental_parsed_50.document
b.bench(fn() {
let result = parse_incremental(
old_doc, incremental_doc_50, new_source, edit,
)
b.keep(result)
})
}
// Baseline: Full re-parse of 100-paragraph document
///|
test "full parse: 100 paragraphs" (b : @bench.T) {
b.bench(fn() {
let result = parse(incremental_doc_100)
b.keep(result)
})
}
// Incremental: Edit middle of 100-paragraph document
///|
test "incremental: 100 paragraphs, edit middle" (b : @bench.T) {
let (new_source, edit) = edited_100
let old_doc = incremental_parsed_100.document
b.bench(fn() {
let result = parse_incremental(
old_doc, incremental_doc_100, new_source, edit,
)
b.keep(result)
})
}
// Edge case: Edit at the beginning
///|
test "incremental: 50 paragraphs, edit start" (b : @bench.T) {
let (new_source, edit) = edited_50_start
let old_doc = incremental_parsed_50.document
b.bench(fn() {
let result = parse_incremental(
old_doc, incremental_doc_50, new_source, edit,
)
b.keep(result)
})
}
// Edge case: Edit at the end
///|
test "incremental: 50 paragraphs, edit end" (b : @bench.T) {
let (new_source, edit) = edited_50_end
let old_doc = incremental_parsed_50.document
b.bench(fn() {
let result = parse_incremental(
old_doc, incremental_doc_50, new_source, edit,
)
b.keep(result)
})
}