Use bitflags for ParseTreeFlags + ParserTestErrorBits

For consistency with simlar code.
This commit is contained in:
Xiretza 2023-05-01 09:07:35 +00:00 committed by Johannes Altmanninger
parent cb368f70ee
commit 1dafb77cda
4 changed files with 56 additions and 91 deletions

View file

@ -14,9 +14,7 @@ use crate::flog::FLOG;
use crate::parse_constants::{ use crate::parse_constants::{
token_type_user_presentable_description, ParseError, ParseErrorCode, ParseErrorList, token_type_user_presentable_description, ParseError, ParseErrorCode, ParseErrorList,
ParseErrorListFfi, ParseKeyword, ParseTokenType, ParseTreeFlags, SourceRange, ParseErrorListFfi, ParseKeyword, ParseTokenType, ParseTreeFlags, SourceRange,
StatementDecoration, INVALID_PIPELINE_CMD_ERR_MSG, PARSE_FLAG_ACCEPT_INCOMPLETE_TOKENS, StatementDecoration, INVALID_PIPELINE_CMD_ERR_MSG, SOURCE_OFFSET_INVALID,
PARSE_FLAG_CONTINUE_AFTER_ERROR, PARSE_FLAG_INCLUDE_COMMENTS, PARSE_FLAG_LEAVE_UNTERMINATED,
PARSE_FLAG_SHOW_EXTRA_SEMIS, SOURCE_OFFSET_INVALID,
}; };
use crate::parse_tree::ParseToken; use crate::parse_tree::ParseToken;
use crate::tokenizer::{ use crate::tokenizer::{
@ -2937,7 +2935,7 @@ impl<'s> Populator<'s> {
fn status(&mut self) -> ParserStatus { fn status(&mut self) -> ParserStatus {
if self.unwinding { if self.unwinding {
ParserStatus::unwinding ParserStatus::unwinding
} else if self.flags & PARSE_FLAG_LEAVE_UNTERMINATED } else if self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
&& self.peek_type(0) == ParseTokenType::terminate && self.peek_type(0) == ParseTokenType::terminate
{ {
ParserStatus::unsourcing ParserStatus::unsourcing
@ -2956,7 +2954,7 @@ impl<'s> Populator<'s> {
/// \return whether we permit an incomplete parse tree. /// \return whether we permit an incomplete parse tree.
fn allow_incomplete(&self) -> bool { fn allow_incomplete(&self) -> bool {
self.flags & PARSE_FLAG_LEAVE_UNTERMINATED self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
} }
/// \return whether a list type \p type allows arbitrary newlines in it. /// \return whether a list type \p type allows arbitrary newlines in it.
@ -3074,7 +3072,7 @@ impl<'s> Populator<'s> {
} else if chomp_semis && peek.typ == ParseTokenType::end && !peek.is_newline { } else if chomp_semis && peek.typ == ParseTokenType::end && !peek.is_newline {
let tok = self.tokens.pop(); let tok = self.tokens.pop();
// Perhaps save this extra semi. // Perhaps save this extra semi.
if self.flags & PARSE_FLAG_SHOW_EXTRA_SEMIS { if self.flags.contains(ParseTreeFlags::SHOW_EXTRA_SEMIS) {
self.semis.push(tok.range()); self.semis.push(tok.range());
} }
} else { } else {
@ -3086,7 +3084,7 @@ impl<'s> Populator<'s> {
/// \return whether a list type should recover from errors.s /// \return whether a list type should recover from errors.s
/// That is, whether we should stop unwinding when we encounter this type. /// That is, whether we should stop unwinding when we encounter this type.
fn list_type_stops_unwind(&self, typ: Type) -> bool { fn list_type_stops_unwind(&self, typ: Type) -> bool {
typ == Type::job_list && self.flags & PARSE_FLAG_CONTINUE_AFTER_ERROR typ == Type::job_list && self.flags.contains(ParseTreeFlags::CONTINUE_AFTER_ERROR)
} }
/// \return a reference to a non-comment token at index \p idx. /// \return a reference to a non-comment token at index \p idx.
@ -3678,7 +3676,7 @@ impl<'s> Populator<'s> {
} }
if !token.allows_token(self.peek_token(0).typ) { if !token.allows_token(self.peek_token(0).typ) {
if self.flags & PARSE_FLAG_LEAVE_UNTERMINATED if self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
&& [ && [
TokenizerError::unterminated_quote, TokenizerError::unterminated_quote,
TokenizerError::unterminated_subshell, TokenizerError::unterminated_subshell,
@ -3714,7 +3712,7 @@ impl<'s> Populator<'s> {
if !keyword.allows_keyword(self.peek_token(0).keyword) { if !keyword.allows_keyword(self.peek_token(0).keyword) {
*keyword.range_mut() = None; *keyword.range_mut() = None;
if self.flags & PARSE_FLAG_LEAVE_UNTERMINATED if self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
&& [ && [
TokenizerError::unterminated_quote, TokenizerError::unterminated_quote,
TokenizerError::unterminated_subshell, TokenizerError::unterminated_subshell,
@ -3842,13 +3840,13 @@ impl From<ParseTreeFlags> for TokFlags {
let mut tok_flags = TokFlags(0); let mut tok_flags = TokFlags(0);
// Note we do not need to respect parse_flag_show_blank_lines, no clients are interested // Note we do not need to respect parse_flag_show_blank_lines, no clients are interested
// in them. // in them.
if flags & PARSE_FLAG_INCLUDE_COMMENTS { if flags.contains(ParseTreeFlags::INCLUDE_COMMENTS) {
tok_flags |= TOK_SHOW_COMMENTS; tok_flags |= TOK_SHOW_COMMENTS;
} }
if flags & PARSE_FLAG_ACCEPT_INCOMPLETE_TOKENS { if flags.contains(ParseTreeFlags::ACCEPT_INCOMPLETE_TOKENS) {
tok_flags |= TOK_ACCEPT_UNFINISHED; tok_flags |= TOK_ACCEPT_UNFINISHED;
} }
if flags & PARSE_FLAG_CONTINUE_AFTER_ERROR { if flags.contains(ParseTreeFlags::CONTINUE_AFTER_ERROR) {
tok_flags |= TOK_CONTINUE_AFTER_ERROR tok_flags |= TOK_CONTINUE_AFTER_ERROR
} }
tok_flags tok_flags
@ -3921,9 +3919,8 @@ fn keyword_for_token(tok: TokenType, token: &wstr) -> ParseKeyword {
use crate::ffi_tests::add_test; use crate::ffi_tests::add_test;
add_test!("test_ast_parse", || { add_test!("test_ast_parse", || {
use crate::parse_constants::PARSE_FLAG_NONE;
let src = L!("echo"); let src = L!("echo");
let ast = Ast::parse(src, PARSE_FLAG_NONE, None); let ast = Ast::parse(src, ParseTreeFlags::empty(), None);
assert!(!ast.any_error); assert!(!ast.any_error);
}); });
@ -4422,7 +4419,7 @@ impl Ast {
fn ast_parse_ffi(src: &CxxWString, flags: u8, errors: *mut ParseErrorListFfi) -> Box<Ast> { fn ast_parse_ffi(src: &CxxWString, flags: u8, errors: *mut ParseErrorListFfi) -> Box<Ast> {
Box::new(Ast::parse( Box::new(Ast::parse(
src.as_wstr(), src.as_wstr(),
ParseTreeFlags(flags), ParseTreeFlags::from_bits(flags).unwrap(),
if errors.is_null() { if errors.is_null() {
None None
} else { } else {
@ -4438,7 +4435,7 @@ fn ast_parse_argument_list_ffi(
) -> Box<Ast> { ) -> Box<Ast> {
Box::new(Ast::parse_argument_list( Box::new(Ast::parse_argument_list(
src.as_wstr(), src.as_wstr(),
ParseTreeFlags(flags), ParseTreeFlags::from_bits(flags).unwrap(),
if errors.is_null() { if errors.is_null() {
None None
} else { } else {

View file

@ -5,9 +5,9 @@ use crate::tokenizer::variable_assignment_equals_pos;
use crate::wchar::{wstr, WString, L}; use crate::wchar::{wstr, WString, L};
use crate::wchar_ffi::{wcharz, AsWstr, WCharFromFFI, WCharToFFI}; use crate::wchar_ffi::{wcharz, AsWstr, WCharFromFFI, WCharToFFI};
use crate::wutil::{sprintf, wgettext_fmt}; use crate::wutil::{sprintf, wgettext_fmt};
use bitflags::bitflags;
use cxx::{type_id, ExternType}; use cxx::{type_id, ExternType};
use cxx::{CxxWString, UniquePtr}; use cxx::{CxxWString, UniquePtr};
use std::ops::{BitAnd, BitOr, BitOrAssign};
use widestring_suffix::widestrs; use widestring_suffix::widestrs;
pub type SourceOffset = u32; pub type SourceOffset = u32;
@ -15,58 +15,30 @@ pub type SourceOffset = u32;
pub const SOURCE_OFFSET_INVALID: usize = SourceOffset::MAX as _; pub const SOURCE_OFFSET_INVALID: usize = SourceOffset::MAX as _;
pub const SOURCE_LOCATION_UNKNOWN: usize = usize::MAX; pub const SOURCE_LOCATION_UNKNOWN: usize = usize::MAX;
#[derive(Copy, Clone)] bitflags! {
pub struct ParseTreeFlags(pub u8); pub struct ParseTreeFlags: u8 {
pub const PARSE_FLAG_NONE: ParseTreeFlags = ParseTreeFlags(0);
/// attempt to build a "parse tree" no matter what. this may result in a 'forest' of /// 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. /// disconnected trees. this is intended to be used by syntax highlighting.
pub const PARSE_FLAG_CONTINUE_AFTER_ERROR: ParseTreeFlags = ParseTreeFlags(1 << 0); const CONTINUE_AFTER_ERROR = 1 << 0;
/// include comment tokens. /// include comment tokens.
pub const PARSE_FLAG_INCLUDE_COMMENTS: ParseTreeFlags = ParseTreeFlags(1 << 1); const INCLUDE_COMMENTS = 1 << 1;
/// indicate that the tokenizer should accept incomplete tokens */ /// indicate that the tokenizer should accept incomplete tokens */
pub const PARSE_FLAG_ACCEPT_INCOMPLETE_TOKENS: ParseTreeFlags = ParseTreeFlags(1 << 2); const ACCEPT_INCOMPLETE_TOKENS = 1 << 2;
/// indicate that the parser should not generate the terminate token, allowing an 'unfinished' /// indicate that the parser should not generate the terminate token, allowing an 'unfinished'
/// tree where some nodes may have no productions. /// tree where some nodes may have no productions.
pub const PARSE_FLAG_LEAVE_UNTERMINATED: ParseTreeFlags = ParseTreeFlags(1 << 3); const LEAVE_UNTERMINATED = 1 << 3;
/// indicate that the parser should generate job_list entries for blank lines. /// indicate that the parser should generate job_list entries for blank lines.
pub const PARSE_FLAG_SHOW_BLANK_LINES: ParseTreeFlags = ParseTreeFlags(1 << 4); const SHOW_BLANK_LINES = 1 << 4;
/// indicate that extra semis should be generated. /// indicate that extra semis should be generated.
pub const PARSE_FLAG_SHOW_EXTRA_SEMIS: ParseTreeFlags = ParseTreeFlags(1 << 5); const SHOW_EXTRA_SEMIS = 1 << 5;
impl BitAnd for ParseTreeFlags {
type Output = bool;
fn bitand(self, rhs: Self) -> Self::Output {
(self.0 & rhs.0) != 0
}
}
impl BitOr for ParseTreeFlags {
type Output = ParseTreeFlags;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl BitOrAssign for ParseTreeFlags {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0
} }
} }
#[derive(PartialEq, Eq, Copy, Clone, Default)] bitflags! {
pub struct ParserTestErrorBits(u8); #[derive(Default)]
pub struct ParserTestErrorBits: u8 {
pub const PARSER_TEST_ERROR: ParserTestErrorBits = ParserTestErrorBits(1); const ERROR = 1;
pub const PARSER_TEST_INCOMPLETE: ParserTestErrorBits = ParserTestErrorBits(2); const INCOMPLETE = 2;
impl BitAnd for ParserTestErrorBits {
type Output = bool;
fn bitand(self, rhs: Self) -> Self::Output {
(self.0 & rhs.0) != 0
}
}
impl BitOrAssign for ParserTestErrorBits {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0
} }
} }

View file

@ -6,8 +6,7 @@ use std::rc::Rc;
use crate::ast::Ast; use crate::ast::Ast;
use crate::parse_constants::{ use crate::parse_constants::{
token_type_user_presentable_description, ParseErrorCode, ParseErrorList, ParseErrorListFfi, token_type_user_presentable_description, ParseErrorCode, ParseErrorList, ParseErrorListFfi,
ParseKeyword, ParseTokenType, ParseTreeFlags, SourceOffset, SourceRange, ParseKeyword, ParseTokenType, ParseTreeFlags, SourceOffset, SourceRange, SOURCE_OFFSET_INVALID,
PARSE_FLAG_CONTINUE_AFTER_ERROR, SOURCE_OFFSET_INVALID,
}; };
use crate::tokenizer::TokenizerError; use crate::tokenizer::TokenizerError;
use crate::wchar::{wstr, WString, L}; use crate::wchar::{wstr, WString, L};
@ -123,7 +122,7 @@ pub fn parse_source(
errors: Option<&mut ParseErrorList>, errors: Option<&mut ParseErrorList>,
) -> ParsedSourceRef { ) -> ParsedSourceRef {
let ast = Ast::parse(&src, flags, errors); let ast = Ast::parse(&src, flags, errors);
if ast.errored() && !(flags & PARSE_FLAG_CONTINUE_AFTER_ERROR) { if ast.errored() && !flags.contains(ParseTreeFlags::CONTINUE_AFTER_ERROR) {
None None
} else { } else {
Some(Rc::new(ParsedSource::new(src, ast))) Some(Rc::new(ParsedSource::new(src, ast)))
@ -179,7 +178,7 @@ fn parse_source_ffi(
) -> Box<ParsedSourceRefFFI> { ) -> Box<ParsedSourceRefFFI> {
Box::new(ParsedSourceRefFFI(parse_source( Box::new(ParsedSourceRefFFI(parse_source(
src.from_ffi(), src.from_ffi(),
ParseTreeFlags(flags), ParseTreeFlags::from_bits(flags).unwrap(),
if errors.is_null() { if errors.is_null() {
None None
} else { } else {

View file

@ -14,14 +14,11 @@ use crate::future_feature_flags::{feature_test, FeatureFlag};
use crate::operation_context::OperationContext; use crate::operation_context::OperationContext;
use crate::parse_constants::{ use crate::parse_constants::{
parse_error_offset_source_start, ParseError, ParseErrorCode, ParseErrorList, ParseKeyword, parse_error_offset_source_start, ParseError, ParseErrorCode, ParseErrorList, ParseKeyword,
ParseTokenType, ParserTestErrorBits, PipelinePosition, StatementDecoration, ParseTokenType, ParseTreeFlags, ParserTestErrorBits, PipelinePosition, StatementDecoration,
ERROR_BAD_VAR_CHAR1, ERROR_BRACKETED_VARIABLE1, ERROR_BRACKETED_VARIABLE_QUOTED1, ERROR_BAD_VAR_CHAR1, ERROR_BRACKETED_VARIABLE1, ERROR_BRACKETED_VARIABLE_QUOTED1,
ERROR_NOT_ARGV_AT, ERROR_NOT_ARGV_COUNT, ERROR_NOT_ARGV_STAR, ERROR_NOT_PID, ERROR_NOT_STATUS, ERROR_NOT_ARGV_AT, ERROR_NOT_ARGV_COUNT, ERROR_NOT_ARGV_STAR, ERROR_NOT_PID, ERROR_NOT_STATUS,
ERROR_NO_VAR_NAME, INVALID_BREAK_ERR_MSG, INVALID_CONTINUE_ERR_MSG, ERROR_NO_VAR_NAME, INVALID_BREAK_ERR_MSG, INVALID_CONTINUE_ERR_MSG,
INVALID_PIPELINE_CMD_ERR_MSG, PARSER_TEST_ERROR, PARSER_TEST_INCOMPLETE, INVALID_PIPELINE_CMD_ERR_MSG, UNKNOWN_BUILTIN_ERR_MSG,
PARSE_FLAG_ACCEPT_INCOMPLETE_TOKENS, PARSE_FLAG_CONTINUE_AFTER_ERROR,
PARSE_FLAG_INCLUDE_COMMENTS, PARSE_FLAG_LEAVE_UNTERMINATED, PARSE_FLAG_NONE,
UNKNOWN_BUILTIN_ERR_MSG,
}; };
use crate::tokenizer::{ use crate::tokenizer::{
comment_end, is_token_delimiter, quote_end, Tok, TokenType, Tokenizer, TOK_ACCEPT_UNFINISHED, comment_end, is_token_delimiter, quote_end, Tok, TokenType, Tokenizer, TOK_ACCEPT_UNFINISHED,
@ -742,10 +739,10 @@ pub fn parse_util_compute_indents(src: &wstr) -> Vec<i32> {
// were a case item list. // were a case item list.
let ast = Ast::parse( let ast = Ast::parse(
src, src,
PARSE_FLAG_CONTINUE_AFTER_ERROR ParseTreeFlags::CONTINUE_AFTER_ERROR
| PARSE_FLAG_INCLUDE_COMMENTS | ParseTreeFlags::INCLUDE_COMMENTS
| PARSE_FLAG_ACCEPT_INCOMPLETE_TOKENS | ParseTreeFlags::ACCEPT_INCOMPLETE_TOKENS
| PARSE_FLAG_LEAVE_UNTERMINATED, | ParseTreeFlags::LEAVE_UNTERMINATED,
None, None,
); );
{ {
@ -965,7 +962,7 @@ impl<'a> NodeVisitor<'a> for IndentVisitor<'a> {
} }
/// Given a string, detect parse errors in it. If allow_incomplete is set, then if the string is /// Given a string, detect parse errors in it. If allow_incomplete is set, then if the string is
/// incomplete (e.g. an unclosed quote), an error is not returned and the PARSER_TEST_INCOMPLETE bit /// incomplete (e.g. an unclosed quote), an error is not returned and the ParserTestErrorBits::INCOMPLETE bit
/// is set in the return value. If allow_incomplete is not set, then incomplete strings result in an /// is set in the return value. If allow_incomplete is not set, then incomplete strings result in an
/// error. /// error.
pub fn parse_util_detect_errors( pub fn parse_util_detect_errors(
@ -978,9 +975,9 @@ pub fn parse_util_detect_errors(
let mut has_unclosed_quote_or_subshell = false; let mut has_unclosed_quote_or_subshell = false;
let parse_flags = if allow_incomplete { let parse_flags = if allow_incomplete {
PARSE_FLAG_LEAVE_UNTERMINATED ParseTreeFlags::LEAVE_UNTERMINATED
} else { } else {
PARSE_FLAG_NONE ParseTreeFlags::empty()
}; };
// Parse the input string into an ast. Some errors are detected here. // Parse the input string into an ast. Some errors are detected here.
@ -1009,14 +1006,14 @@ pub fn parse_util_detect_errors(
assert!(!has_unclosed_quote_or_subshell || allow_incomplete); assert!(!has_unclosed_quote_or_subshell || allow_incomplete);
if has_unclosed_quote_or_subshell { if has_unclosed_quote_or_subshell {
// We do not bother to validate the rest of the tree in this case. // We do not bother to validate the rest of the tree in this case.
return Err(PARSER_TEST_INCOMPLETE); return Err(ParserTestErrorBits::INCOMPLETE);
} }
// Early parse error, stop here. // Early parse error, stop here.
if !parse_errors.is_empty() { if !parse_errors.is_empty() {
if let Some(errors) = out_errors.as_mut() { if let Some(errors) = out_errors.as_mut() {
errors.extend(parse_errors.into_iter()); errors.extend(parse_errors.into_iter());
return Err(PARSER_TEST_ERROR); return Err(ParserTestErrorBits::ERROR);
} }
} }
@ -1107,11 +1104,11 @@ pub fn parse_util_detect_errors_in_ast(
} }
if errored { if errored {
res |= PARSER_TEST_ERROR; res |= ParserTestErrorBits::ERROR;
} }
if has_unclosed_block || has_unclosed_pipe || has_unclosed_conjunction { if has_unclosed_block || has_unclosed_pipe || has_unclosed_conjunction {
res |= PARSER_TEST_INCOMPLETE; res |= ParserTestErrorBits::INCOMPLETE;
} }
if res == ParserTestErrorBits::default() { if res == ParserTestErrorBits::default() {
Ok(()) Ok(())
@ -1139,7 +1136,7 @@ pub fn parse_util_detect_errors_in_argument_list(
// Parse the string as a freestanding argument list. // Parse the string as a freestanding argument list.
let mut errors = ParseErrorList::new(); let mut errors = ParseErrorList::new();
let ast = Ast::parse_argument_list(arg_list_src, PARSE_FLAG_NONE, Some(&mut errors)); let ast = Ast::parse_argument_list(arg_list_src, ParseTreeFlags::empty(), Some(&mut errors));
if !errors.is_empty() { if !errors.is_empty() {
return get_error_text(&errors); return get_error_text(&errors);
} }
@ -1219,13 +1216,13 @@ pub fn parse_util_detect_errors_in_argument(
append_syntax_error!( append_syntax_error!(
out_errors, source_start + begin, end - begin, out_errors, source_start + begin, end - begin,
"Incomplete escape sequence '%ls'", arg_src); "Incomplete escape sequence '%ls'", arg_src);
return PARSER_TEST_ERROR; return ParserTestErrorBits::ERROR;
} }
append_syntax_error!( append_syntax_error!(
out_errors, source_start + begin, end - begin, out_errors, source_start + begin, end - begin,
"Invalid token '%ls'", arg_src); "Invalid token '%ls'", arg_src);
} }
return PARSER_TEST_ERROR; return ParserTestErrorBits::ERROR;
}; };
let mut err = ParserTestErrorBits::default(); let mut err = ParserTestErrorBits::default();
@ -1239,7 +1236,7 @@ pub fn parse_util_detect_errors_in_argument(
if ![VARIABLE_EXPAND, VARIABLE_EXPAND_SINGLE, '('].contains(&next_char) if ![VARIABLE_EXPAND, VARIABLE_EXPAND_SINGLE, '('].contains(&next_char)
&& !valid_var_name_char(next_char) && !valid_var_name_char(next_char)
{ {
err = PARSER_TEST_ERROR; err = ParserTestErrorBits::ERROR;
if let Some(ref mut out_errors) = out_errors { if let Some(ref mut out_errors) = out_errors {
let mut first_dollar = idx; let mut first_dollar = idx;
while first_dollar > 0 while first_dollar > 0
@ -1282,7 +1279,7 @@ pub fn parse_util_detect_errors_in_argument(
Some(&mut has_dollar), Some(&mut has_dollar),
) { ) {
-1 => { -1 => {
err |= PARSER_TEST_ERROR; err |= ParserTestErrorBits::ERROR;
append_syntax_error!(out_errors, source_start, 1, "Mismatched parenthesis"); append_syntax_error!(out_errors, source_start, 1, "Mismatched parenthesis");
return err; return err;
} }