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::{
token_type_user_presentable_description, ParseError, ParseErrorCode, ParseErrorList,
ParseErrorListFfi, ParseKeyword, ParseTokenType, ParseTreeFlags, SourceRange,
StatementDecoration, INVALID_PIPELINE_CMD_ERR_MSG, PARSE_FLAG_ACCEPT_INCOMPLETE_TOKENS,
PARSE_FLAG_CONTINUE_AFTER_ERROR, PARSE_FLAG_INCLUDE_COMMENTS, PARSE_FLAG_LEAVE_UNTERMINATED,
PARSE_FLAG_SHOW_EXTRA_SEMIS, SOURCE_OFFSET_INVALID,
StatementDecoration, INVALID_PIPELINE_CMD_ERR_MSG, SOURCE_OFFSET_INVALID,
};
use crate::parse_tree::ParseToken;
use crate::tokenizer::{
@ -2937,7 +2935,7 @@ impl<'s> Populator<'s> {
fn status(&mut self) -> ParserStatus {
if self.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
{
ParserStatus::unsourcing
@ -2956,7 +2954,7 @@ impl<'s> Populator<'s> {
/// \return whether we permit an incomplete parse tree.
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.
@ -3074,7 +3072,7 @@ impl<'s> Populator<'s> {
} else if chomp_semis && peek.typ == ParseTokenType::end && !peek.is_newline {
let tok = self.tokens.pop();
// 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());
}
} else {
@ -3086,7 +3084,7 @@ impl<'s> Populator<'s> {
/// \return whether a list type should recover from errors.s
/// That is, whether we should stop unwinding when we encounter this type.
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.
@ -3678,7 +3676,7 @@ impl<'s> Populator<'s> {
}
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_subshell,
@ -3714,7 +3712,7 @@ impl<'s> Populator<'s> {
if !keyword.allows_keyword(self.peek_token(0).keyword) {
*keyword.range_mut() = None;
if self.flags & PARSE_FLAG_LEAVE_UNTERMINATED
if self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
&& [
TokenizerError::unterminated_quote,
TokenizerError::unterminated_subshell,
@ -3842,13 +3840,13 @@ impl From<ParseTreeFlags> for TokFlags {
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 & PARSE_FLAG_INCLUDE_COMMENTS {
if flags.contains(ParseTreeFlags::INCLUDE_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;
}
if flags & PARSE_FLAG_CONTINUE_AFTER_ERROR {
if flags.contains(ParseTreeFlags::CONTINUE_AFTER_ERROR) {
tok_flags |= TOK_CONTINUE_AFTER_ERROR
}
tok_flags
@ -3921,9 +3919,8 @@ fn keyword_for_token(tok: TokenType, token: &wstr) -> ParseKeyword {
use crate::ffi_tests::add_test;
add_test!("test_ast_parse", || {
use crate::parse_constants::PARSE_FLAG_NONE;
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);
});
@ -4422,7 +4419,7 @@ impl Ast {
fn ast_parse_ffi(src: &CxxWString, flags: u8, errors: *mut ParseErrorListFfi) -> Box<Ast> {
Box::new(Ast::parse(
src.as_wstr(),
ParseTreeFlags(flags),
ParseTreeFlags::from_bits(flags).unwrap(),
if errors.is_null() {
None
} else {
@ -4438,7 +4435,7 @@ fn ast_parse_argument_list_ffi(
) -> Box<Ast> {
Box::new(Ast::parse_argument_list(
src.as_wstr(),
ParseTreeFlags(flags),
ParseTreeFlags::from_bits(flags).unwrap(),
if errors.is_null() {
None
} else {

View file

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

View file

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

View file

@ -14,14 +14,11 @@ use crate::future_feature_flags::{feature_test, FeatureFlag};
use crate::operation_context::OperationContext;
use crate::parse_constants::{
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_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,
INVALID_PIPELINE_CMD_ERR_MSG, PARSER_TEST_ERROR, PARSER_TEST_INCOMPLETE,
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,
INVALID_PIPELINE_CMD_ERR_MSG, UNKNOWN_BUILTIN_ERR_MSG,
};
use crate::tokenizer::{
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.
let ast = Ast::parse(
src,
PARSE_FLAG_CONTINUE_AFTER_ERROR
| PARSE_FLAG_INCLUDE_COMMENTS
| PARSE_FLAG_ACCEPT_INCOMPLETE_TOKENS
| PARSE_FLAG_LEAVE_UNTERMINATED,
ParseTreeFlags::CONTINUE_AFTER_ERROR
| ParseTreeFlags::INCLUDE_COMMENTS
| ParseTreeFlags::ACCEPT_INCOMPLETE_TOKENS
| ParseTreeFlags::LEAVE_UNTERMINATED,
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
/// 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
/// error.
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 parse_flags = if allow_incomplete {
PARSE_FLAG_LEAVE_UNTERMINATED
ParseTreeFlags::LEAVE_UNTERMINATED
} else {
PARSE_FLAG_NONE
ParseTreeFlags::empty()
};
// 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);
if has_unclosed_quote_or_subshell {
// 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.
if !parse_errors.is_empty() {
if let Some(errors) = out_errors.as_mut() {
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 {
res |= PARSER_TEST_ERROR;
res |= ParserTestErrorBits::ERROR;
}
if has_unclosed_block || has_unclosed_pipe || has_unclosed_conjunction {
res |= PARSER_TEST_INCOMPLETE;
res |= ParserTestErrorBits::INCOMPLETE;
}
if res == ParserTestErrorBits::default() {
Ok(())
@ -1139,7 +1136,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, PARSE_FLAG_NONE, Some(&mut errors));
let ast = Ast::parse_argument_list(arg_list_src, ParseTreeFlags::empty(), Some(&mut errors));
if !errors.is_empty() {
return get_error_text(&errors);
}
@ -1219,13 +1216,13 @@ pub fn parse_util_detect_errors_in_argument(
append_syntax_error!(
out_errors, source_start + begin, end - begin,
"Incomplete escape sequence '%ls'", arg_src);
return PARSER_TEST_ERROR;
return ParserTestErrorBits::ERROR;
}
append_syntax_error!(
out_errors, source_start + begin, end - begin,
"Invalid token '%ls'", arg_src);
}
return PARSER_TEST_ERROR;
return ParserTestErrorBits::ERROR;
};
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)
&& !valid_var_name_char(next_char)
{
err = PARSER_TEST_ERROR;
err = ParserTestErrorBits::ERROR;
if let Some(ref mut out_errors) = out_errors {
let mut first_dollar = idx;
while first_dollar > 0
@ -1282,7 +1279,7 @@ pub fn parse_util_detect_errors_in_argument(
Some(&mut has_dollar),
) {
-1 => {
err |= PARSER_TEST_ERROR;
err |= ParserTestErrorBits::ERROR;
append_syntax_error!(out_errors, source_start, 1, "Mismatched parenthesis");
return err;
}