Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b8bd4df
Re-emit branch_point captures on fail-retry
stefanobaghino Apr 20, 2026
1ee0e3c
Decrement Haskell syntest baseline after capture re-emit fix
stefanobaghino Apr 20, 2026
b8e4936
Fix MSRV rustfmt and docs broken intra-doc links
stefanobaghino Apr 20, 2026
c183165
Restore cleared scopes before Pop on pop: N + set: X
stefanobaghino Apr 20, 2026
d1200b3
Clip captures: N span to rule's match bounds
stefanobaghino Apr 21, 2026
cd23cc9
Suppress cur meta_scope on pop: N + embed: match text
stefanobaghino Apr 21, 2026
e9c022a
Drain orphan scope atoms on outer escape fire
stefanobaghino Apr 22, 2026
b185a2c
Order apply_prototype external prototype before target main
stefanobaghino Apr 22, 2026
69cd4b3
Cover apply_prototype external-prototype precedence
stefanobaghino Apr 23, 2026
347ea76
Drop HAML-Rails from syntest baselines
stefanobaghino Apr 23, 2026
17e46c4
Wrap past-EOL syntest assertions to next-line scopes
stefanobaghino Apr 23, 2026
b1b5438
Dedup flushed_ops across multi-fail parse_line calls
stefanobaghino Apr 23, 2026
78da6c4
Anchor replay-born branches to their replay line
stefanobaghino Apr 23, 2026
715a68e
Inherit outer prefix in replay-born branch_points
stefanobaghino Apr 25, 2026
7706736
Refresh post-replay shadow and stack_before snapshots
stefanobaghino Apr 25, 2026
d31dbe3
Subtract bp.pop_count in branch_point retain predicate
stefanobaghino Apr 26, 2026
d4a7e05
Route handle_fail cross-line re-emit through push_meta_ops
stefanobaghino Apr 26, 2026
82703dc
Save flushed_ops before cross-line replay loop and prefer inner corre…
stefanobaghino Apr 26, 2026
f3e497a
Fall through to parent on branch_point exhaustion
stefanobaghino Apr 27, 2026
ed949e4
Restore cleared scopes before deeper pops in pop:N
stefanobaghino Apr 27, 2026
0a2139a
Skip inner replay correction when nested deeper than outer BP
stefanobaghino Apr 27, 2026
d36dc01
Treat pop:N + push:X as Set with pop_count=N
stefanobaghino Apr 27, 2026
b31b727
Skip cur mcs Pop in Set when wrapper has embed_scope_replaces
stefanobaghino Apr 27, 2026
4bd9272
Defer target Clear in single-context Set when cur has meta_scope
stefanobaghino Apr 27, 2026
96ae959
Drop extra parent mcs on trigger of multi-set + target clear+ms
stefanobaghino Apr 27, 2026
aca11f5
Restore deeper frame clear_scopes on pop:N + set:
stefanobaghino Apr 27, 2026
61a796e
Drop python_strings.py from syntest baselines
stefanobaghino Apr 27, 2026
3b44840
Skip embed_scope_replaces for fragment embeds
stefanobaghino Apr 27, 2026
d4b6a5b
Drop python.py from syntest baselines
stefanobaghino Apr 27, 2026
72f5ac0
Prefer own over inherited in multi-extends parent merge
stefanobaghino Apr 28, 2026
172a697
Skip non-pure assertion lines in the syntest harness
stefanobaghino Apr 28, 2026
230faae
Bypass search cache on truncated searches at embed-escape boundary
stefanobaghino Apr 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified assets/default_newlines.packdump
Binary file not shown.
Binary file modified assets/default_nonewlines.packdump
Binary file not shown.
397 changes: 320 additions & 77 deletions examples/syntest.rs

Large diffs are not rendered by default.

2,962 changes: 2,771 additions & 191 deletions src/parsing/parser.rs

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions src/parsing/regex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,4 +345,27 @@ mod tests {
let back_to_str = serde_json::to_string(&pattern).unwrap();
assert_eq!(back_to_str, "\"just a string\"");
}

// The parser uses `end < text.len()` to clip a search at an embed-escape
// boundary. Negative lookaheads inside the embedded context's rules must
// treat the boundary as effective end-of-input — otherwise patterns like
// `done(?!cmd_char)` fail when `done` is immediately followed by the
// closing escape glyph (e.g. `` `for...done` `` in shell).
#[test]
fn search_clips_lookahead_at_end() {
let regex = Regex::new(String::from(r"done(?!X)"));
let mut region = Region::new();
let matched = regex.search("doneX", 0, 4, Some(&mut region), true);
assert!(matched, "lookahead must not see byte at index 4");
assert_eq!(region.pos(0), Some((0, 4)));
}

// Sanity check: when not clipped, the same lookahead correctly fails.
#[test]
fn search_lookahead_fails_when_unclipped() {
let regex = Regex::new(String::from(r"done(?!X)"));
let mut region = Region::new();
let matched = regex.search("doneX", 0, 5, Some(&mut region), true);
assert!(!matched, "lookahead must see the X when end == text.len()");
}
}
11 changes: 7 additions & 4 deletions src/parsing/syntax_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,17 +274,20 @@ impl<'a> Iterator for MatchIter<'a> {
_ => return self.next(),
};
let ctx_ptr = self.syntax_set.get_context(context_id).unwrap();
// Also include the external syntax's prototype if the context allows it
// Push target first, then external prototype on top so
// its patterns are iterated first: `MatchIter::next`
// reads the stack top, and ST's `apply_prototype` runs
// the prototype ahead of the included context — mirror
// of `ParseState::find_best_match`'s prototype chaining.
self.ctx_stack.push(ctx_ptr);
self.index_stack.push(0);
if ctx_ptr.meta_include_prototype.unwrap_or(true) {
if let Some(ref proto_id) = ctx_ptr.prototype {
let proto_ctx = self.syntax_set.get_context(proto_id).unwrap();
// Push prototype first (it will be iterated first)
self.ctx_stack.push(proto_ctx);
self.index_stack.push(0);
}
}
self.ctx_stack.push(ctx_ptr);
self.index_stack.push(0);
}
}
} else {
Expand Down
Loading