mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-19 04:41:15 -03:00
Make ParseTreeFlags an ordinary struct instead of bitflags
Simplifies some code.
This commit is contained in:
41
src/ast.rs
41
src/ast.rs
@@ -1964,9 +1964,7 @@ fn spaces(&self) -> usize {
|
||||
fn status(&mut self) -> ParserStatus {
|
||||
if self.unwinding {
|
||||
ParserStatus::unwinding
|
||||
} else if self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
|
||||
&& self.peek_type(0) == ParseTokenType::Terminate
|
||||
{
|
||||
} else if self.flags.leave_unterminated && self.peek_type(0) == ParseTokenType::Terminate {
|
||||
ParserStatus::unsourcing
|
||||
} else {
|
||||
ParserStatus::ok
|
||||
@@ -1983,7 +1981,7 @@ fn unsource_leaves(&mut self) -> bool {
|
||||
|
||||
/// Return whether we permit an incomplete parse tree.
|
||||
fn allow_incomplete(&self) -> bool {
|
||||
self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
|
||||
self.flags.leave_unterminated
|
||||
}
|
||||
|
||||
/// Return whether a list kind allows arbitrary newlines in it.
|
||||
@@ -2100,7 +2098,7 @@ fn chomp_extras(&mut self, kind: Kind) {
|
||||
} else if chomp_semis && peek.typ == ParseTokenType::End && !peek.is_newline {
|
||||
let tok = self.tokens.pop();
|
||||
// Perhaps save this extra semi.
|
||||
if self.flags.contains(ParseTreeFlags::SHOW_EXTRA_SEMIS) {
|
||||
if self.flags.show_extra_semis {
|
||||
self.semis.push(tok.range());
|
||||
}
|
||||
} else {
|
||||
@@ -2112,8 +2110,7 @@ fn chomp_extras(&mut self, kind: Kind) {
|
||||
/// Return whether a list kind should recover from errors.
|
||||
/// That is, whether we should stop unwinding when we encounter this type.
|
||||
fn list_kind_stops_unwind(&self, kind: Kind) -> bool {
|
||||
matches!(kind, Kind::JobList(_))
|
||||
&& self.flags.contains(ParseTreeFlags::CONTINUE_AFTER_ERROR)
|
||||
matches!(kind, Kind::JobList(_)) && self.flags.continue_after_error
|
||||
}
|
||||
|
||||
/// Return a reference to a non-comment token at index `idx`.
|
||||
@@ -2643,12 +2640,11 @@ fn visit_token(&mut self, token: &mut dyn Token) {
|
||||
}
|
||||
|
||||
if !token.allows_token(self.peek_token(0).typ) {
|
||||
if self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
|
||||
&& [
|
||||
TokenizerError::UnterminatedQuote,
|
||||
TokenizerError::UnterminatedSubshell,
|
||||
]
|
||||
.contains(&self.peek_token(0).tok_error)
|
||||
if self.flags.leave_unterminated
|
||||
&& matches!(
|
||||
self.peek_token(0).tok_error,
|
||||
TokenizerError::UnterminatedQuote | TokenizerError::UnterminatedSubshell
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -2679,12 +2675,11 @@ fn visit_keyword(&mut self, keyword: &mut dyn Keyword) -> VisitResult {
|
||||
if !keyword.allows_keyword(self.peek_token(0).keyword) {
|
||||
*keyword.range_mut() = None;
|
||||
|
||||
if self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
|
||||
&& [
|
||||
TokenizerError::UnterminatedQuote,
|
||||
TokenizerError::UnterminatedSubshell,
|
||||
]
|
||||
.contains(&self.peek_token(0).tok_error)
|
||||
if self.flags.leave_unterminated
|
||||
&& matches!(
|
||||
self.peek_token(0).tok_error,
|
||||
TokenizerError::UnterminatedQuote | TokenizerError::UnterminatedSubshell
|
||||
)
|
||||
{
|
||||
return VisitResult::Continue(());
|
||||
}
|
||||
@@ -2754,13 +2749,13 @@ fn from(flags: ParseTreeFlags) -> Self {
|
||||
let mut tok_flags = TokFlags(0);
|
||||
// Note we do not need to respect parse_flag_show_blank_lines, no clients are interested
|
||||
// in them.
|
||||
if flags.contains(ParseTreeFlags::INCLUDE_COMMENTS) {
|
||||
if flags.include_comments {
|
||||
tok_flags |= TOK_SHOW_COMMENTS;
|
||||
}
|
||||
if flags.contains(ParseTreeFlags::ACCEPT_INCOMPLETE_TOKENS) {
|
||||
if flags.accept_incomplete_tokens {
|
||||
tok_flags |= TOK_ACCEPT_UNFINISHED;
|
||||
}
|
||||
if flags.contains(ParseTreeFlags::CONTINUE_AFTER_ERROR) {
|
||||
if flags.continue_after_error {
|
||||
tok_flags |= TOK_CONTINUE_AFTER_ERROR
|
||||
}
|
||||
tok_flags
|
||||
@@ -2837,7 +2832,7 @@ mod tests {
|
||||
fn test_ast_parse() {
|
||||
let _cleanup = test_init();
|
||||
let src = L!("echo");
|
||||
let ast = ast::parse(src, ParseTreeFlags::empty(), None);
|
||||
let ast = ast::parse(src, ParseTreeFlags::default(), None);
|
||||
assert!(!ast.any_error);
|
||||
}
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ fn run_command_list(parser: &Parser, cmds: &[OsString]) -> Result<(), libc::c_in
|
||||
let cmd_wcs = bytes2wcstring(cmd.as_bytes());
|
||||
|
||||
let mut errors = ParseErrorList::new();
|
||||
let ast = ast::parse(&cmd_wcs, ParseTreeFlags::empty(), Some(&mut errors));
|
||||
let ast = ast::parse(&cmd_wcs, ParseTreeFlags::default(), Some(&mut errors));
|
||||
let errored = ast.errored() || {
|
||||
parse_util_detect_errors_in_ast(&ast, &cmd_wcs, Some(&mut errors)).is_err()
|
||||
};
|
||||
|
||||
@@ -103,11 +103,12 @@ fn strip_dollar_prefixes(insert_mode: AppendMode, prefix: &wstr, insert: &wstr)
|
||||
}
|
||||
insert.find(L!("$ "))?; // Early return.
|
||||
let source = prefix.to_owned() + insert;
|
||||
let ast = ast::parse(
|
||||
&source,
|
||||
ParseTreeFlags::ACCEPT_INCOMPLETE_TOKENS | ParseTreeFlags::LEAVE_UNTERMINATED,
|
||||
None,
|
||||
);
|
||||
let flags = ParseTreeFlags {
|
||||
accept_incomplete_tokens: true,
|
||||
leave_unterminated: true,
|
||||
..Default::default()
|
||||
};
|
||||
let ast = ast::parse(&source, flags, None);
|
||||
let mut stripped = WString::new();
|
||||
let mut have = prefix.len();
|
||||
for node in ast.walk() {
|
||||
|
||||
@@ -896,10 +896,13 @@ fn prettify_traversal(&mut self) {
|
||||
|
||||
// The flags we use to parse.
|
||||
fn parse_flags() -> ParseTreeFlags {
|
||||
ParseTreeFlags::CONTINUE_AFTER_ERROR
|
||||
| ParseTreeFlags::INCLUDE_COMMENTS
|
||||
| ParseTreeFlags::LEAVE_UNTERMINATED
|
||||
| ParseTreeFlags::SHOW_BLANK_LINES
|
||||
ParseTreeFlags {
|
||||
continue_after_error: true,
|
||||
include_comments: true,
|
||||
leave_unterminated: true,
|
||||
show_blank_lines: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return whether a character at a given index is escaped.
|
||||
@@ -1304,13 +1307,13 @@ struct TokenRange {
|
||||
// Entry point for prettification.
|
||||
fn prettify(streams: &mut IoStreams, src: &wstr, do_indent: bool) -> WString {
|
||||
if DUMP_PARSE_TREE.load() {
|
||||
let ast = ast::parse(
|
||||
src,
|
||||
ParseTreeFlags::LEAVE_UNTERMINATED
|
||||
| ParseTreeFlags::INCLUDE_COMMENTS
|
||||
| ParseTreeFlags::SHOW_EXTRA_SEMIS,
|
||||
None,
|
||||
);
|
||||
let flags = ParseTreeFlags {
|
||||
leave_unterminated: true,
|
||||
include_comments: true,
|
||||
show_extra_semis: true,
|
||||
..Default::default()
|
||||
};
|
||||
let ast = ast::parse(src, flags, None);
|
||||
let ast_dump = ast.dump(src);
|
||||
streams.err.appendln(ast_dump);
|
||||
|
||||
|
||||
@@ -300,11 +300,12 @@ fn autosuggest_parse_command(
|
||||
buff: &wstr,
|
||||
ctx: &OperationContext<'_>,
|
||||
) -> Option<(WString, WString)> {
|
||||
let ast = ast::parse(
|
||||
buff,
|
||||
ParseTreeFlags::CONTINUE_AFTER_ERROR | ParseTreeFlags::ACCEPT_INCOMPLETE_TOKENS,
|
||||
None,
|
||||
);
|
||||
let flags = ParseTreeFlags {
|
||||
continue_after_error: true,
|
||||
accept_incomplete_tokens: true,
|
||||
..Default::default()
|
||||
};
|
||||
let ast = ast::parse(buff, flags, None);
|
||||
|
||||
// Find the first statement.
|
||||
let job_list: &ast::JobList = ast.top();
|
||||
@@ -742,11 +743,14 @@ pub fn highlight(&mut self) -> ColorArray {
|
||||
.resize(self.buff.len(), HighlightSpec::default());
|
||||
|
||||
// Flags we use for AST parsing.
|
||||
let ast_flags = ParseTreeFlags::CONTINUE_AFTER_ERROR
|
||||
| ParseTreeFlags::INCLUDE_COMMENTS
|
||||
| ParseTreeFlags::ACCEPT_INCOMPLETE_TOKENS
|
||||
| ParseTreeFlags::LEAVE_UNTERMINATED
|
||||
| ParseTreeFlags::SHOW_EXTRA_SEMIS;
|
||||
let ast_flags = ParseTreeFlags {
|
||||
continue_after_error: true,
|
||||
include_comments: true,
|
||||
accept_incomplete_tokens: true,
|
||||
leave_unterminated: true,
|
||||
show_extra_semis: true,
|
||||
..Default::default()
|
||||
};
|
||||
let ast = ast::parse(self.buff, ast_flags, None);
|
||||
|
||||
self.visit_children(ast.top());
|
||||
|
||||
@@ -1179,7 +1179,7 @@ fn should_import_bash_history_line(line: &wstr) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
if ast::parse(line, ParseTreeFlags::empty(), None).errored() {
|
||||
if ast::parse(line, ParseTreeFlags::default(), None).errored() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1264,7 +1264,7 @@ pub fn add_pending_with_file_detection(
|
||||
|
||||
// Find all arguments that look like they could be file paths.
|
||||
let mut needs_sync_write = false;
|
||||
let ast = ast::parse(s, ParseTreeFlags::empty(), None);
|
||||
let ast = ast::parse(s, ParseTreeFlags::default(), None);
|
||||
|
||||
let mut potential_paths = Vec::new();
|
||||
for node in ast.walk() {
|
||||
|
||||
@@ -9,24 +9,22 @@
|
||||
pub const SOURCE_OFFSET_INVALID: usize = SourceOffset::MAX as _;
|
||||
pub const SOURCE_LOCATION_UNKNOWN: usize = usize::MAX;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct ParseTreeFlags: u8 {
|
||||
/// attempt to build a "parse tree" no matter what. this may result in a 'forest' of
|
||||
/// disconnected trees. this is intended to be used by syntax highlighting.
|
||||
const CONTINUE_AFTER_ERROR = 1 << 0;
|
||||
/// include comment tokens.
|
||||
const INCLUDE_COMMENTS = 1 << 1;
|
||||
/// indicate that the tokenizer should accept incomplete tokens
|
||||
const ACCEPT_INCOMPLETE_TOKENS = 1 << 2;
|
||||
/// indicate that the parser should not generate the terminate token, allowing an 'unfinished'
|
||||
/// tree where some nodes may have no productions.
|
||||
const LEAVE_UNTERMINATED = 1 << 3;
|
||||
/// indicate that the parser should generate job_list entries for blank lines.
|
||||
const SHOW_BLANK_LINES = 1 << 4;
|
||||
/// indicate that extra semis should be generated.
|
||||
const SHOW_EXTRA_SEMIS = 1 << 5;
|
||||
}
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct ParseTreeFlags {
|
||||
/// attempt to build a "parse tree" no matter what. this may result in a 'forest' of
|
||||
/// disconnected trees. this is intended to be used by syntax highlighting.
|
||||
pub continue_after_error: bool,
|
||||
/// include comment tokens.
|
||||
pub include_comments: bool,
|
||||
/// indicate that the tokenizer should accept incomplete tokens
|
||||
pub accept_incomplete_tokens: bool,
|
||||
/// indicate that the parser should not generate the terminate token, allowing an 'unfinished'
|
||||
/// tree where some nodes may have no productions.
|
||||
pub leave_unterminated: bool,
|
||||
/// indicate that the parser should generate job_list entries for blank lines.
|
||||
pub show_blank_lines: bool,
|
||||
/// indicate that extra semis should be generated.
|
||||
pub show_extra_semis: bool,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
|
||||
@@ -204,7 +204,7 @@ pub fn parse_source(
|
||||
errors: Option<&mut ParseErrorList>,
|
||||
) -> Option<ParsedSourceRef> {
|
||||
let ast = ast::parse(&src, flags, errors);
|
||||
if ast.errored() && !flags.contains(ParseTreeFlags::CONTINUE_AFTER_ERROR) {
|
||||
if ast.errored() && !flags.continue_after_error {
|
||||
None
|
||||
} else {
|
||||
Some(Arc::new(ParsedSource::new(src, ast)))
|
||||
|
||||
@@ -775,14 +775,14 @@ fn compute_indents(src: &wstr, initial_indent: i32) -> Vec<i32> {
|
||||
// the last node we visited becomes the input indent of the next. I.e. in the case of 'switch
|
||||
// foo ; cas', we get an invalid parse tree (since 'cas' is not valid) but we indent it as if it
|
||||
// were a case item list.
|
||||
let ast = ast::parse(
|
||||
src,
|
||||
ParseTreeFlags::CONTINUE_AFTER_ERROR
|
||||
| ParseTreeFlags::INCLUDE_COMMENTS
|
||||
| ParseTreeFlags::ACCEPT_INCOMPLETE_TOKENS
|
||||
| ParseTreeFlags::LEAVE_UNTERMINATED,
|
||||
None,
|
||||
);
|
||||
let flags = ParseTreeFlags {
|
||||
continue_after_error: true,
|
||||
include_comments: true,
|
||||
accept_incomplete_tokens: true,
|
||||
leave_unterminated: true,
|
||||
..Default::default()
|
||||
};
|
||||
let ast = ast::parse(src, flags, None);
|
||||
{
|
||||
let mut iv = IndentVisitor::new(src, &mut indents, initial_indent);
|
||||
iv.visit(ast.top());
|
||||
@@ -1133,10 +1133,9 @@ pub fn parse_util_detect_errors(
|
||||
// allow_incomplete is set.
|
||||
let mut has_unclosed_quote_or_subshell = false;
|
||||
|
||||
let parse_flags = if allow_incomplete {
|
||||
ParseTreeFlags::LEAVE_UNTERMINATED
|
||||
} else {
|
||||
ParseTreeFlags::empty()
|
||||
let parse_flags = ParseTreeFlags {
|
||||
leave_unterminated: allow_incomplete,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Parse the input string into an ast. Some errors are detected here.
|
||||
@@ -1336,7 +1335,7 @@ pub fn parse_util_detect_errors_in_argument_list(
|
||||
|
||||
// Parse the string as a freestanding argument list.
|
||||
let mut errors = ParseErrorList::new();
|
||||
let ast = ast::parse_argument_list(arg_list_src, ParseTreeFlags::empty(), Some(&mut errors));
|
||||
let ast = ast::parse_argument_list(arg_list_src, ParseTreeFlags::default(), Some(&mut errors));
|
||||
if !errors.is_empty() {
|
||||
return get_error_text(&errors);
|
||||
}
|
||||
|
||||
@@ -528,7 +528,7 @@ pub fn eval_with(
|
||||
let mut error_list = ParseErrorList::new();
|
||||
if let Some(ps) = parse_source(
|
||||
cmd.to_owned(),
|
||||
ParseTreeFlags::empty(),
|
||||
ParseTreeFlags::default(),
|
||||
Some(&mut error_list),
|
||||
) {
|
||||
return self.eval_parsed_source(
|
||||
@@ -600,7 +600,7 @@ pub fn eval_wstr(
|
||||
use crate::parse_tree::ParsedSource;
|
||||
use crate::parse_util::parse_util_detect_errors_in_ast;
|
||||
let mut errors = vec![];
|
||||
let ast = ast::parse(&src, ParseTreeFlags::empty(), Some(&mut errors));
|
||||
let ast = ast::parse(&src, ParseTreeFlags::default(), Some(&mut errors));
|
||||
let mut errored = ast.errored();
|
||||
if !errored {
|
||||
errored = parse_util_detect_errors_in_ast(&ast, &src, Some(&mut errors)).is_err();
|
||||
@@ -2051,18 +2051,19 @@ fn test_new_parser_ad_hoc() {
|
||||
let ast = ast::parse(L!("a="), ParseTreeFlags::default(), None);
|
||||
assert!(ast.errored());
|
||||
|
||||
let flags = ParseTreeFlags {
|
||||
leave_unterminated: true,
|
||||
..ParseTreeFlags::default()
|
||||
};
|
||||
|
||||
// If we are leaving things unterminated, this should not produce an error.
|
||||
// i.e. when typing "a=" at the command line, it should be treated as valid
|
||||
// because we don't want to color it as an error.
|
||||
let ast = ast::parse(L!("a="), ParseTreeFlags::LEAVE_UNTERMINATED, None);
|
||||
let ast = ast::parse(L!("a="), flags, None);
|
||||
assert!(!ast.errored());
|
||||
|
||||
let mut errors = vec![];
|
||||
ast::parse(
|
||||
L!("begin; echo ("),
|
||||
ParseTreeFlags::LEAVE_UNTERMINATED,
|
||||
Some(&mut errors),
|
||||
);
|
||||
ast::parse(L!("begin; echo ("), flags, Some(&mut errors));
|
||||
assert_eq!(errors.len(), 1);
|
||||
assert_eq!(
|
||||
errors[0].code,
|
||||
@@ -2070,11 +2071,7 @@ fn test_new_parser_ad_hoc() {
|
||||
);
|
||||
|
||||
errors.clear();
|
||||
ast::parse(
|
||||
L!("for x in ("),
|
||||
ParseTreeFlags::LEAVE_UNTERMINATED,
|
||||
Some(&mut errors),
|
||||
);
|
||||
ast::parse(L!("for x in ("), flags, Some(&mut errors));
|
||||
assert_eq!(errors.len(), 1);
|
||||
assert_eq!(
|
||||
errors[0].code,
|
||||
@@ -2082,11 +2079,7 @@ fn test_new_parser_ad_hoc() {
|
||||
);
|
||||
|
||||
errors.clear();
|
||||
ast::parse(
|
||||
L!("begin; echo '"),
|
||||
ParseTreeFlags::LEAVE_UNTERMINATED,
|
||||
Some(&mut errors),
|
||||
);
|
||||
ast::parse(L!("begin; echo '"), flags, Some(&mut errors));
|
||||
assert_eq!(errors.len(), 1);
|
||||
assert_eq!(errors[0].code, ParseErrorCode::TokenizerUnterminatedQuote);
|
||||
}
|
||||
@@ -2395,7 +2388,11 @@ fn get_parents<'s>(
|
||||
#[test]
|
||||
fn test_ast() {
|
||||
// Light testing of the AST and traversals.
|
||||
let ast = ast::parse(TrueSemiAstTester::TRUE_SEMI, ParseTreeFlags::empty(), None);
|
||||
let ast = ast::parse(
|
||||
TrueSemiAstTester::TRUE_SEMI,
|
||||
ParseTreeFlags::default(),
|
||||
None,
|
||||
);
|
||||
let tester = TrueSemiAstTester::new(&ast);
|
||||
|
||||
// Walk the AST and collect all nodes.
|
||||
@@ -2456,7 +2453,7 @@ fn test_ast() {
|
||||
#[should_panic]
|
||||
fn test_traversal_skip_children_panics() {
|
||||
// Test that we panic if we try to skip children of a node that is not the current node.
|
||||
let ast = ast::parse(L!("true;"), ParseTreeFlags::empty(), None);
|
||||
let ast = ast::parse(L!("true;"), ParseTreeFlags::default(), None);
|
||||
let mut traversal = ast.walk();
|
||||
while let Some(node) = traversal.next() {
|
||||
if matches!(node.kind(), ast::Kind::DecoratedStatement(_)) {
|
||||
@@ -2470,7 +2467,7 @@ fn test_traversal_skip_children_panics() {
|
||||
#[should_panic]
|
||||
fn test_traversal_parent_panics() {
|
||||
// Can only get the parent of nodes still on the stack.
|
||||
let ast = ast::parse(L!("true;"), ParseTreeFlags::empty(), None);
|
||||
let ast = ast::parse(L!("true;"), ParseTreeFlags::default(), None);
|
||||
let mut traversal = ast.walk();
|
||||
let mut decorated_statement = None;
|
||||
while let Some(node) = traversal.next() {
|
||||
|
||||
@@ -5959,9 +5959,12 @@ struct PositionedToken {
|
||||
}
|
||||
|
||||
fn extract_tokens(s: &wstr) -> Vec<PositionedToken> {
|
||||
let ast_flags = ParseTreeFlags::CONTINUE_AFTER_ERROR
|
||||
| ParseTreeFlags::ACCEPT_INCOMPLETE_TOKENS
|
||||
| ParseTreeFlags::LEAVE_UNTERMINATED;
|
||||
let ast_flags = ParseTreeFlags {
|
||||
continue_after_error: true,
|
||||
accept_incomplete_tokens: true,
|
||||
leave_unterminated: true,
|
||||
..ParseTreeFlags::default()
|
||||
};
|
||||
let ast = ast::parse(s, ast_flags, None);
|
||||
|
||||
let mut result = vec![];
|
||||
|
||||
Reference in New Issue
Block a user