use super::*;
impl<'a> Parser<'a> {
pub fn new(input: &'a str) -> Self {
Self::with_limits_and_profile(
input,
DEFAULT_MAX_AST_DEPTH,
DEFAULT_MAX_PARSER_OPERATIONS,
ShellProfile::native(ShellDialect::Bash),
)
}
pub fn with_dialect(input: &'a str, dialect: ShellDialect) -> Self {
Self::with_profile(input, ShellProfile::native(dialect))
}
pub fn with_profile(input: &'a str, shell_profile: ShellProfile) -> Self {
Self::with_limits_and_profile(
input,
DEFAULT_MAX_AST_DEPTH,
DEFAULT_MAX_PARSER_OPERATIONS,
shell_profile,
)
}
pub fn with_fuel(input: &'a str, max_fuel: usize) -> Self {
Self::with_limits_and_profile(
input,
DEFAULT_MAX_AST_DEPTH,
max_fuel,
ShellProfile::native(ShellDialect::Bash),
)
}
pub fn with_limits_and_profile(
input: &'a str,
max_depth: usize,
max_fuel: usize,
shell_profile: ShellProfile,
) -> Self {
Self::with_limits_and_profile_and_benchmarking(
input,
max_depth,
max_fuel,
shell_profile,
false,
)
}
pub(super) fn with_limits_and_profile_and_benchmarking(
input: &'a str,
max_depth: usize,
max_fuel: usize,
shell_profile: ShellProfile,
benchmark_counters_enabled: bool,
) -> Self {
#[cfg(not(feature = "benchmarking"))]
let _ = benchmark_counters_enabled;
let zsh_timeline = (shell_profile.dialect == ShellDialect::Zsh)
.then(|| ZshOptionTimeline::build(input, &shell_profile))
.flatten()
.map(Arc::new);
let mut lexer = Lexer::with_max_subst_depth_and_profile(
input,
max_depth.min(HARD_MAX_AST_DEPTH),
&shell_profile,
zsh_timeline.clone(),
);
#[cfg(feature = "benchmarking")]
if benchmark_counters_enabled {
lexer.enable_benchmark_counters();
}
let mut comments = Vec::new();
let (current_token, current_token_kind, current_keyword, current_span) = loop {
match lexer.next_lexed_token_with_comments() {
Some(st) if st.kind == TokenKind::Comment => {
comments.push(Comment {
range: st.span.to_range(),
});
}
Some(st) => {
break (
Some(st.clone()),
Some(st.kind),
Self::keyword_from_token(&st),
st.span,
);
}
None => break (None, None, None, Span::new()),
}
};
Self {
input,
lexer,
synthetic_tokens: VecDeque::new(),
alias_replays: Vec::new(),
current_token,
current_word_cache: None,
current_token_kind,
current_keyword,
current_span,
peeked_token: None,
max_depth: max_depth.min(HARD_MAX_AST_DEPTH),
current_depth: 0,
fuel: max_fuel,
max_fuel,
source_text_pattern_depth: 0,
comments,
aliases: HashMap::new(),
expand_aliases: false,
expand_next_word: false,
brace_group_depth: 0,
brace_body_stack: Vec::new(),
syntax_facts: SyntaxFacts::default(),
dialect: shell_profile.dialect,
shell_profile,
zsh_timeline,
brace_scan_chars: std::cell::RefCell::new(Vec::new()),
#[cfg(feature = "benchmarking")]
benchmark_counters: benchmark_counters_enabled.then(ParserBenchmarkCounters::default),
}
}
#[cfg(feature = "benchmarking")]
pub(super) fn rebuild_with_benchmark_counters(&self) -> Self {
Self::with_limits_and_profile_and_benchmarking(
self.input,
self.max_depth,
self.max_fuel,
self.shell_profile.clone(),
true,
)
}
#[cfg(test)]
pub(super) fn current_span(&self) -> Span {
self.current_span
}
pub fn parse_word_string(input: &str) -> Word {
let mut parser = Parser::new(input);
let start = Position::new();
parser.parse_word_with_context(
input,
Span::from_positions(start, start.advanced_by(input)),
start,
true,
)
}
pub fn parse_assignment_word_group(
source: &str,
words: &[&Word],
explicit_array_kind: Option<ArrayKind>,
subscript_interpretation: SubscriptInterpretation,
) -> Option<Assignment> {
let first = words.first()?;
let last = words.last()?;
let span = Span::from_positions(first.span.start, last.span.end);
let raw = span.slice(source);
let mut parser = Parser::new(source);
parser.parse_assignment_from_text(raw, span, explicit_array_kind, subscript_interpretation)
}
pub(super) fn parse_word_string_with_limits_and_dialect(
input: &str,
max_depth: usize,
max_fuel: usize,
dialect: ShellDialect,
) -> Word {
let mut parser = Parser::with_limits_and_profile(
input,
max_depth,
max_fuel,
ShellProfile::native(dialect),
);
let start = Position::new();
parser.parse_word_with_context(
input,
Span::from_positions(start, start.advanced_by(input)),
start,
true,
)
}
#[cfg(test)]
pub(super) fn parse_word_fragment(source: &str, text: &str, span: Span) -> Word {
Self::parse_word_fragment_with_limits(
source,
text,
span,
DEFAULT_MAX_AST_DEPTH,
DEFAULT_MAX_PARSER_OPERATIONS,
ShellProfile::native(ShellDialect::Bash),
)
}
pub(super) fn parse_word_fragment_with_limits(
source: &str,
text: &str,
span: Span,
max_depth: usize,
max_fuel: usize,
shell_profile: ShellProfile,
) -> Word {
let mut parser = Parser::with_limits_and_profile(text, max_depth, max_fuel, shell_profile);
let source_backed = span.end.offset() <= source.len() && span.slice(source) == text;
let start = Position::new();
let fragment_span = Span::from_positions(start, start.advanced_by(text));
let mut word = parser.parse_word_with_context(text, fragment_span, start, source_backed);
if !source_backed {
Self::materialize_word_source_backing(&mut word, text);
}
Self::rebase_word(&mut word, span.start);
word.span = span;
word
}
}