1use super::*;
2
3impl<'a> Parser<'a> {
4 pub fn new(input: &'a str) -> Self {
6 Self::with_limits_and_profile(
7 input,
8 DEFAULT_MAX_AST_DEPTH,
9 DEFAULT_MAX_PARSER_OPERATIONS,
10 ShellProfile::native(ShellDialect::Bash),
11 )
12 }
13
14 pub fn with_dialect(input: &'a str, dialect: ShellDialect) -> Self {
19 Self::with_profile(input, ShellProfile::native(dialect))
20 }
21
22 pub fn with_profile(input: &'a str, shell_profile: ShellProfile) -> Self {
27 Self::with_limits_and_profile(
28 input,
29 DEFAULT_MAX_AST_DEPTH,
30 DEFAULT_MAX_PARSER_OPERATIONS,
31 shell_profile,
32 )
33 }
34
35 pub fn with_max_depth(input: &'a str, max_depth: usize) -> Self {
40 Self::with_limits_and_profile(
41 input,
42 max_depth,
43 DEFAULT_MAX_PARSER_OPERATIONS,
44 ShellProfile::native(ShellDialect::Bash),
45 )
46 }
47
48 pub fn with_fuel(input: &'a str, max_fuel: usize) -> Self {
53 Self::with_limits_and_profile(
54 input,
55 DEFAULT_MAX_AST_DEPTH,
56 max_fuel,
57 ShellProfile::native(ShellDialect::Bash),
58 )
59 }
60
61 pub fn with_limits(input: &'a str, max_depth: usize, max_fuel: usize) -> Self {
67 Self::with_limits_and_profile(
68 input,
69 max_depth,
70 max_fuel,
71 ShellProfile::native(ShellDialect::Bash),
72 )
73 }
74
75 pub fn with_limits_and_dialect(
81 input: &'a str,
82 max_depth: usize,
83 max_fuel: usize,
84 dialect: ShellDialect,
85 ) -> Self {
86 Self::with_limits_and_profile(input, max_depth, max_fuel, ShellProfile::native(dialect))
87 }
88
89 pub fn with_limits_and_profile(
94 input: &'a str,
95 max_depth: usize,
96 max_fuel: usize,
97 shell_profile: ShellProfile,
98 ) -> Self {
99 Self::with_limits_and_profile_and_benchmarking(
100 input,
101 max_depth,
102 max_fuel,
103 shell_profile,
104 false,
105 )
106 }
107
108 pub(super) fn with_limits_and_profile_and_benchmarking(
109 input: &'a str,
110 max_depth: usize,
111 max_fuel: usize,
112 shell_profile: ShellProfile,
113 benchmark_counters_enabled: bool,
114 ) -> Self {
115 #[cfg(not(feature = "benchmarking"))]
116 let _ = benchmark_counters_enabled;
117
118 let zsh_timeline = (shell_profile.dialect == ShellDialect::Zsh)
119 .then(|| ZshOptionTimeline::build(input, &shell_profile))
120 .flatten()
121 .map(Arc::new);
122 let mut lexer = Lexer::with_max_subst_depth_and_profile(
123 input,
124 max_depth.min(HARD_MAX_AST_DEPTH),
125 &shell_profile,
126 zsh_timeline.clone(),
127 );
128 #[cfg(feature = "benchmarking")]
129 if benchmark_counters_enabled {
130 lexer.enable_benchmark_counters();
131 }
132 let mut comments = Vec::new();
133 let (current_token, current_token_kind, current_keyword, current_span) = loop {
134 match lexer.next_lexed_token_with_comments() {
135 Some(st) if st.kind == TokenKind::Comment => {
136 comments.push(Comment {
137 range: st.span.to_range(),
138 });
139 }
140 Some(st) => {
141 break (
142 Some(st.clone()),
143 Some(st.kind),
144 Self::keyword_from_token(&st),
145 st.span,
146 );
147 }
148 None => break (None, None, None, Span::new()),
149 }
150 };
151 Self {
152 input,
153 lexer,
154 synthetic_tokens: VecDeque::new(),
155 alias_replays: Vec::new(),
156 current_token,
157 current_word_cache: None,
158 current_token_kind,
159 current_keyword,
160 current_span,
161 peeked_token: None,
162 max_depth: max_depth.min(HARD_MAX_AST_DEPTH),
163 current_depth: 0,
164 fuel: max_fuel,
165 max_fuel,
166 source_text_pattern_depth: 0,
167 comments,
168 aliases: HashMap::new(),
169 expand_aliases: false,
170 expand_next_word: false,
171 brace_group_depth: 0,
172 brace_body_stack: Vec::new(),
173 syntax_facts: SyntaxFacts::default(),
174 dialect: shell_profile.dialect,
175 shell_profile,
176 zsh_timeline,
177 #[cfg(feature = "benchmarking")]
178 benchmark_counters: benchmark_counters_enabled.then(ParserBenchmarkCounters::default),
179 }
180 }
181
182 #[cfg(feature = "benchmarking")]
183 pub(super) fn rebuild_with_benchmark_counters(&self) -> Self {
184 Self::with_limits_and_profile_and_benchmarking(
185 self.input,
186 self.max_depth,
187 self.max_fuel,
188 self.shell_profile.clone(),
189 true,
190 )
191 }
192
193 #[cfg(test)]
194 pub(super) fn current_span(&self) -> Span {
195 self.current_span
196 }
197
198 pub fn parse_word_string(input: &str) -> Word {
204 let mut parser = Parser::new(input);
205 let start = Position::new();
206 parser.parse_word_with_context(
207 input,
208 Span::from_positions(start, start.advanced_by(input)),
209 start,
210 true,
211 )
212 }
213
214 pub fn parse_assignment_word_group(
220 source: &str,
221 words: &[&Word],
222 explicit_array_kind: Option<ArrayKind>,
223 subscript_interpretation: SubscriptInterpretation,
224 ) -> Option<Assignment> {
225 let first = words.first()?;
226 let last = words.last()?;
227 let span = Span::from_positions(first.span.start, last.span.end);
228 let raw = span.slice(source);
229 let mut parser = Parser::new(source);
230 parser.parse_assignment_from_text(raw, span, explicit_array_kind, subscript_interpretation)
231 }
232
233 pub(super) fn parse_word_string_with_limits_and_dialect(
235 input: &str,
236 max_depth: usize,
237 max_fuel: usize,
238 dialect: ShellDialect,
239 ) -> Word {
240 let mut parser = Parser::with_limits_and_profile(
241 input,
242 max_depth,
243 max_fuel,
244 ShellProfile::native(dialect),
245 );
246 let start = Position::new();
247 parser.parse_word_with_context(
248 input,
249 Span::from_positions(start, start.advanced_by(input)),
250 start,
251 true,
252 )
253 }
254
255 #[cfg(test)]
258 pub(super) fn parse_word_fragment(source: &str, text: &str, span: Span) -> Word {
259 Self::parse_word_fragment_with_limits(
260 source,
261 text,
262 span,
263 DEFAULT_MAX_AST_DEPTH,
264 DEFAULT_MAX_PARSER_OPERATIONS,
265 ShellProfile::native(ShellDialect::Bash),
266 )
267 }
268
269 pub(super) fn parse_word_fragment_with_limits(
270 source: &str,
271 text: &str,
272 span: Span,
273 max_depth: usize,
274 max_fuel: usize,
275 shell_profile: ShellProfile,
276 ) -> Word {
277 let mut parser = Parser::with_limits_and_profile(text, max_depth, max_fuel, shell_profile);
278 let source_backed = span.end.offset <= source.len() && span.slice(source) == text;
279 let start = Position::new();
280 let fragment_span = Span::from_positions(start, start.advanced_by(text));
281 let mut word = parser.parse_word_with_context(text, fragment_span, start, source_backed);
282 if !source_backed {
283 Self::materialize_word_source_backing(&mut word, text);
284 }
285 Self::rebase_word(&mut word, span.start);
286 word.span = span;
287 word
288 }
289}