1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! Owned structural captures over the production streaming PEG executor.
use super::{Arena, ParseResult, Parser};
use crate::{Result, invalid};
/// A named rule or tagged byte extent in a PEG parse tree.
#[derive(Clone, Debug)]
pub struct Capture {
/// Rule name, empty for tag wrappers.
pub name: String,
/// Semantic tag, empty for ordinary named rules.
pub tag: String,
/// Initial byte offset.
pub start: usize,
/// Exclusive byte offset reached by the capture.
pub end: usize,
/// Whether this node represents an incomplete input prefix.
pub incomplete: bool,
/// Captures nested under this rule or tag in source traversal order.
pub children: Vec<Self>,
}
/// Parse status together with its reachable rule and tag nodes.
#[derive(Clone, Debug)]
pub struct CapturedParse {
/// Production streaming parse extent and status.
pub result: ParseResult,
/// Root captures in source traversal order.
pub nodes: Vec<Capture>,
}
impl Arena {
/// Parse arbitrary bytes and retain rule/tag extents, optionally starting within input.
///
/// # Errors
/// Returns an error for invalid offsets/flags or input, recursion and work limits.
pub fn parse_captured(&self, bytes: &[u8], flags: u8, start: usize) -> Result<CapturedParse> {
if bytes.len() > 1024 * 1024 || flags > 3 || start > bytes.len() {
return Err(invalid("PEG capture input/flags/offset outside bound"));
}
self.capture_at(self.root, bytes, flags & 1 != 0, start, 0, &mut 0)
}
fn capture_at(
&self,
id: usize,
bytes: &[u8],
lenient: bool,
start: usize,
depth: usize,
steps: &mut usize,
) -> Result<CapturedParse> {
let result = self.parse_at(id, bytes, lenient, start, depth, steps)?;
let mut nodes = Vec::new();
if result.result_type == 0 {
return Ok(CapturedParse { result, nodes });
}
let child = |id, start, steps: &mut usize| {
self.capture_at(id, bytes, lenient, start, depth + 1, steps)
};
let parser = &self.parsers[id];
match parser {
Parser::Sequence { children } => {
let mut offset = start;
for &id in children {
let parsed = child(id, offset, steps)?;
nodes.extend(parsed.nodes);
offset = parsed.result.end;
if parsed.result.result_type != 1 {
break;
}
}
}
Parser::Choice { children } => {
for &id in children {
let parsed = child(id, start, steps)?;
if parsed.result.result_type != 0 {
nodes = parsed.nodes;
break;
}
}
}
Parser::Repetition {
child: id,
max_count,
..
} => {
let mut offset = start;
let mut count = 0;
while (*max_count < 0 || count < *max_count) && offset < bytes.len() {
let parsed = child(*id, offset, steps)?;
if parsed.result.result_type == 0 {
break;
}
nodes.extend(parsed.nodes);
if parsed.result.result_type == 2 || parsed.result.end == offset {
break;
}
offset = parsed.result.end;
count += 1;
}
}
Parser::Rule {
child: id, name, ..
}
| Parser::Tag {
child: id,
tag: name,
} => {
let parsed = child(*id, start, steps)?;
nodes.push(Capture {
name: if matches!(parser, Parser::Rule { .. }) {
name.clone()
} else {
String::new()
},
tag: if matches!(parser, Parser::Tag { .. }) {
name.clone()
} else {
String::new()
},
start: result.start,
end: result.end,
incomplete: result.result_type == 2,
children: parsed.nodes,
});
}
Parser::Atomic { child: id } => {
if result.result_type != 2 {
nodes = child(*id, start, steps)?.nodes;
}
}
Parser::Gbnf { child: id, .. }
| Parser::Ac { child: id, .. }
| Parser::Schema { child: id, .. } => nodes = child(*id, start, steps)?.nodes,
Parser::Ref { name } => {
nodes = child(
*self
.rules
.get(name)
.ok_or_else(|| invalid("PEG rule reference missing"))?,
start,
steps,
)?
.nodes;
}
_ => {}
}
if nodes.len() > 65536 {
return Err(invalid("PEG capture node bound exceeded"));
}
Ok(CapturedParse { result, nodes })
}
}