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_fuel(input: &'a str, max_fuel: usize) -> Self {
40 Self::with_limits_and_profile(
41 input,
42 DEFAULT_MAX_AST_DEPTH,
43 max_fuel,
44 ShellProfile::native(ShellDialect::Bash),
45 )
46 }
47
48 pub fn with_limits_and_profile(
53 input: &'a str,
54 max_depth: usize,
55 max_fuel: usize,
56 shell_profile: ShellProfile,
57 ) -> Self {
58 Self::with_limits_and_profile_and_benchmarking(
59 input,
60 max_depth,
61 max_fuel,
62 shell_profile,
63 false,
64 )
65 }
66
67 pub(super) fn with_limits_and_profile_and_benchmarking(
68 input: &'a str,
69 max_depth: usize,
70 max_fuel: usize,
71 shell_profile: ShellProfile,
72 benchmark_counters_enabled: bool,
73 ) -> Self {
74 #[cfg(not(feature = "benchmarking"))]
75 let _ = benchmark_counters_enabled;
76
77 let zsh_timeline = (shell_profile.dialect == ShellDialect::Zsh)
78 .then(|| ZshOptionTimeline::build(input, &shell_profile))
79 .flatten()
80 .map(Arc::new);
81 let mut lexer = Lexer::with_max_subst_depth_and_profile(
82 input,
83 max_depth.min(HARD_MAX_AST_DEPTH),
84 &shell_profile,
85 zsh_timeline.clone(),
86 );
87 #[cfg(feature = "benchmarking")]
88 if benchmark_counters_enabled {
89 lexer.enable_benchmark_counters();
90 }
91 let mut comments = Vec::new();
92 let (current_token, current_token_kind, current_keyword, current_span) = loop {
93 match lexer.next_lexed_token_with_comments() {
94 Some(st) if st.kind == TokenKind::Comment => {
95 comments.push(Comment {
96 range: st.span.to_range(),
97 });
98 }
99 Some(st) => {
100 break (
101 Some(st.clone()),
102 Some(st.kind),
103 Self::keyword_from_token(&st),
104 st.span,
105 );
106 }
107 None => break (None, None, None, Span::new()),
108 }
109 };
110 Self {
111 input,
112 lexer,
113 synthetic_tokens: VecDeque::new(),
114 alias_replays: Vec::new(),
115 current_token,
116 current_word_cache: None,
117 current_token_kind,
118 current_keyword,
119 current_span,
120 peeked_token: None,
121 max_depth: max_depth.min(HARD_MAX_AST_DEPTH),
122 current_depth: 0,
123 fuel: max_fuel,
124 max_fuel,
125 source_text_pattern_depth: 0,
126 comments,
127 aliases: HashMap::new(),
128 expand_aliases: false,
129 expand_next_word: false,
130 brace_group_depth: 0,
131 brace_body_stack: Vec::new(),
132 syntax_facts: SyntaxFacts::default(),
133 dialect: shell_profile.dialect,
134 shell_profile,
135 zsh_timeline,
136 brace_scan_chars: std::cell::RefCell::new(Vec::new()),
137 #[cfg(feature = "benchmarking")]
138 benchmark_counters: benchmark_counters_enabled.then(ParserBenchmarkCounters::default),
139 }
140 }
141
142 #[cfg(feature = "benchmarking")]
143 pub(super) fn rebuild_with_benchmark_counters(&self) -> Self {
144 Self::with_limits_and_profile_and_benchmarking(
145 self.input,
146 self.max_depth,
147 self.max_fuel,
148 self.shell_profile.clone(),
149 true,
150 )
151 }
152
153 #[cfg(test)]
154 pub(super) fn current_span(&self) -> Span {
155 self.current_span
156 }
157
158 pub fn parse_word_string(input: &str) -> Word {
164 let mut parser = Parser::new(input);
165 let start = Position::new();
166 parser.parse_word_with_context(
167 input,
168 Span::from_positions(start, start.advanced_by(input)),
169 start,
170 true,
171 )
172 }
173
174 pub fn parse_assignment_word_group(
180 source: &str,
181 words: &[&Word],
182 explicit_array_kind: Option<ArrayKind>,
183 subscript_interpretation: SubscriptInterpretation,
184 ) -> Option<Assignment> {
185 let first = words.first()?;
186 let last = words.last()?;
187 let span = Span::from_positions(first.span.start, last.span.end);
188 let raw = span.slice(source);
189 let mut parser = Parser::new(source);
190 parser.parse_assignment_from_text(raw, span, explicit_array_kind, subscript_interpretation)
191 }
192
193 pub(super) fn parse_word_string_with_limits_and_dialect(
195 input: &str,
196 max_depth: usize,
197 max_fuel: usize,
198 dialect: ShellDialect,
199 ) -> Word {
200 let mut parser = Parser::with_limits_and_profile(
201 input,
202 max_depth,
203 max_fuel,
204 ShellProfile::native(dialect),
205 );
206 let start = Position::new();
207 parser.parse_word_with_context(
208 input,
209 Span::from_positions(start, start.advanced_by(input)),
210 start,
211 true,
212 )
213 }
214
215 #[cfg(test)]
218 pub(super) fn parse_word_fragment(source: &str, text: &str, span: Span) -> Word {
219 Self::parse_word_fragment_with_limits(
220 source,
221 text,
222 span,
223 DEFAULT_MAX_AST_DEPTH,
224 DEFAULT_MAX_PARSER_OPERATIONS,
225 ShellProfile::native(ShellDialect::Bash),
226 )
227 }
228
229 pub(super) fn parse_word_fragment_with_limits(
230 source: &str,
231 text: &str,
232 span: Span,
233 max_depth: usize,
234 max_fuel: usize,
235 shell_profile: ShellProfile,
236 ) -> Word {
237 let mut parser = Parser::with_limits_and_profile(text, max_depth, max_fuel, shell_profile);
238 let source_backed = span.end.offset() <= source.len() && span.slice(source) == text;
239 let start = Position::new();
240 let fragment_span = Span::from_positions(start, start.advanced_by(text));
241 let mut word = parser.parse_word_with_context(text, fragment_span, start, source_backed);
242 if !source_backed {
243 Self::materialize_word_source_backing(&mut word, text);
244 }
245 Self::rebase_word(&mut word, span.start);
246 word.span = span;
247 word
248 }
249}