Port parse_util

Except for the indent visitor bits.

Tests for parse_util_detect_errors* are not ported yet because they depend
on expand.h (and operation_context.h which depends on env.h).
This commit is contained in:
Johannes Altmanninger 2023-04-18 12:53:32 +02:00
parent 36ba912779
commit 12afb320a3
7 changed files with 1659 additions and 70 deletions

View File

@ -151,6 +151,10 @@ pub trait Node: Acceptor + ConcreteNode + std::fmt::Debug {
// The address of the object, for comparison.
fn as_ptr(&self) -> *const ();
fn pointer_eq(&self, rhs: &dyn Node) -> bool {
std::ptr::eq(self.as_ptr(), rhs.as_ptr())
}
}
/// NodeMut is a mutable node.
@ -626,9 +630,12 @@ macro_rules! define_list_node {
}
impl $name {
/// Iteration support.
fn iter<F>(&self) -> impl Iterator<Item = &<$name as List>::ContentsNode> {
pub fn iter(&self) -> impl Iterator<Item = &<$name as List>::ContentsNode> {
self.contents().iter().map(|b| &**b)
}
pub fn get(&self, index: usize) -> Option<&$contents> {
self.contents().get(index).map(|b| &**b)
}
}
impl Index<usize> for $name {
type Output = <$name as List>::ContentsNode;

View File

@ -1,7 +1,51 @@
use crate::common::{char_offset, EXPAND_RESERVED_BASE, EXPAND_RESERVED_END};
use crate::wchar::wstr;
use crate::operation_context::OperationContext;
use crate::parse_constants::ParseErrorList;
use crate::wchar::{wstr, WString};
use bitflags::bitflags;
use widestring_suffix::widestrs;
bitflags! {
/// Set of flags controlling expansions.
pub struct ExpandFlags : u16 {
/// Skip command substitutions.
const SKIP_CMDSUBST = 1 << 0;
/// Skip variable expansion.
const SKIP_VARIABLES = 1 << 1;
/// Skip wildcard expansion.
const SKIP_WILDCARDS = 1 << 2;
/// The expansion is being done for tab or auto completions. Returned completions may have the
/// wildcard as a prefix instead of a match.
const FOR_COMPLETIONS = 1 << 3;
/// Only match files that are executable by the current user.
const EXECUTABLES_ONLY = 1 << 4;
/// Only match directories.
const DIRECTORIES_ONLY = 1 << 5;
/// Generate descriptions, stored in the description field of completions.
const GEN_DESCRIPTIONS = 1 << 6;
/// Un-expand home directories to tildes after.
const PRESERVE_HOME_TILDES = 1 << 7;
/// Allow fuzzy matching.
const FUZZY_MATCH = 1 << 8;
/// Disallow directory abbreviations like /u/l/b for /usr/local/bin. Only applicable if
/// fuzzy_match is set.
const NO_FUZZY_DIRECTORIES = 1 << 9;
/// Allows matching a leading dot even if the wildcard does not contain one.
/// By default, wildcards only match a leading dot literally; this is why e.g. '*' does not
/// match hidden files.
const ALLOW_NONLITERAL_LEADING_DOT = 1 << 10;
/// Do expansions specifically to support cd. This means using CDPATH as a list of potential
/// working directories, and to use logical instead of physical paths.
const SPECIAL_FOR_CD = 1 << 11;
/// Do expansions specifically for cd autosuggestion. This is to differentiate between cd
/// completions and cd autosuggestions.
const SPECIAL_FOR_CD_AUTOSUGGESTION = 1 << 12;
/// Do expansions specifically to support external command completions. This means using PATH as
/// a list of potential working directories.
const SPECIAL_FOR_COMMAND = 1 << 13;
}
}
/// Character representing a home directory.
pub const HOME_DIRECTORY: char = char_offset(EXPAND_RESERVED_BASE, 0);
/// Character representing process expansion for %self.
@ -29,6 +73,70 @@ const _: () = assert!(
"Characters used in expansions must stay within private use area"
);
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum ExpandResultCode {
/// There was an error, for example, unmatched braces.
error,
/// Expansion succeeded.
ok,
/// Expansion was cancelled (e.g. control-C).
cancel,
/// Expansion succeeded, but a wildcard in the string matched no files,
/// so the output is empty.
wildcard_no_match,
}
/// These are the possible return values for expand_string.
pub struct ExpandResult {
// todo!
pub result: ExpandResultCode,
}
impl PartialEq<ExpandResultCode> for ExpandResult {
fn eq(&self, other: &ExpandResultCode) -> bool {
self.result == *other
}
}
/// The string represented by PROCESS_EXPAND_SELF
#[widestrs]
pub const PROCESS_EXPAND_SELF_STR: &wstr = "%self"L;
/// expand_one is identical to expand_string, except it will fail if in expands to more than one
/// string. This is used for expanding command names.
///
/// \param inout_str The parameter to expand in-place
/// \param flags Specifies if any expansion pass should be skipped. Legal values are any combination
/// of skip_cmdsubst skip_variables and skip_wildcards
/// \param ctx The parser, variables, and cancellation checker for this operation. The parser may be
/// null. \param errors Resulting errors, or nullptr to ignore
///
/// \return Whether expansion succeeded.
#[allow(unused_variables)]
pub fn expand_one(
s: &mut WString,
flags: ExpandFlags,
ctx: &OperationContext,
errors: Option<&mut ParseErrorList>,
) -> bool {
todo!()
}
/// Expand a command string like $HOME/bin/cmd into a command and list of arguments.
/// Return the command and arguments by reference.
/// If the expansion resulted in no or an empty command, the command will be an empty string. Note
/// that API does not distinguish between expansion resulting in an empty command (''), and
/// expansion resulting in no command (e.g. unset variable).
/// If \p skip_wildcards is true, then do not do wildcard expansion
/// \return an expand error.
#[allow(unused_variables)]
pub fn expand_to_command_and_args(
instr: &wstr,
ctx: &OperationContext,
out_cmd: &mut WString,
out_args: Option<&Vec<WString>>,
errors: &mut ParseErrorList,
skip_wildcards: bool,
) -> ExpandResult {
todo!()
}

View File

@ -6,6 +6,7 @@
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::uninlined_format_args)]
#![allow(clippy::derivable_impls)]
#![allow(clippy::option_map_unit_fn)]
#[macro_use]
mod common;
@ -40,6 +41,7 @@ mod job_group;
mod locale;
mod nix;
mod null_terminated_array;
mod operation_context;
mod parse_constants;
mod parse_tree;
mod parse_util;

View File

@ -0,0 +1,7 @@
pub struct OperationContext {}
impl OperationContext {
pub fn empty() -> OperationContext {
todo!()
}
}

View File

@ -52,7 +52,7 @@ impl BitOrAssign for ParseTreeFlags {
}
}
#[derive(PartialEq, Eq, Copy, Clone)]
#[derive(PartialEq, Eq, Copy, Clone, Default)]
pub struct ParserTestErrorBits(u8);
pub const PARSER_TEST_ERROR: ParserTestErrorBits = ParserTestErrorBits(1);

File diff suppressed because it is too large Load Diff

View File

@ -439,38 +439,6 @@ static void test_escape_crazy() {
}
}
static void test_escape_quotes() {
say(L"Testing escaping with quotes");
// These are "raw string literals"
do_test(parse_util_escape_string_with_quote(L"abc", L'\0') == L"abc");
do_test(parse_util_escape_string_with_quote(L"abc~def", L'\0') == L"abc\\~def");
do_test(parse_util_escape_string_with_quote(L"abc~def", L'\0', true) == L"abc~def");
do_test(parse_util_escape_string_with_quote(L"abc\\~def", L'\0') == L"abc\\\\\\~def");
do_test(parse_util_escape_string_with_quote(L"abc\\~def", L'\0', true) == L"abc\\\\~def");
do_test(parse_util_escape_string_with_quote(L"~abc", L'\0') == L"\\~abc");
do_test(parse_util_escape_string_with_quote(L"~abc", L'\0', true) == L"~abc");
do_test(parse_util_escape_string_with_quote(L"~abc|def", L'\0') == L"\\~abc\\|def");
do_test(parse_util_escape_string_with_quote(L"|abc~def", L'\0') == L"\\|abc\\~def");
do_test(parse_util_escape_string_with_quote(L"|abc~def", L'\0', true) == L"\\|abc~def");
do_test(parse_util_escape_string_with_quote(L"foo\nbar", L'\0') == L"foo\\nbar");
// Note tildes are not expanded inside quotes, so no_tilde is ignored with a quote.
do_test(parse_util_escape_string_with_quote(L"abc", L'\'') == L"abc");
do_test(parse_util_escape_string_with_quote(L"abc\\def", L'\'') == L"abc\\\\def");
do_test(parse_util_escape_string_with_quote(L"abc'def", L'\'') == L"abc\\'def");
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'\'') == L"~abc\\'def");
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'\'', true) == L"~abc\\'def");
do_test(parse_util_escape_string_with_quote(L"foo\nba'r", L'\'') == L"foo'\\n'ba\\'r");
do_test(parse_util_escape_string_with_quote(L"foo\\\\bar", L'\'') == L"foo\\\\\\\\bar");
do_test(parse_util_escape_string_with_quote(L"abc", L'"') == L"abc");
do_test(parse_util_escape_string_with_quote(L"abc\\def", L'"') == L"abc\\\\def");
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'"') == L"~abc'def");
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'"', true) == L"~abc'def");
do_test(parse_util_escape_string_with_quote(L"foo\nba'r", L'"') == L"foo\"\\n\"ba'r");
do_test(parse_util_escape_string_with_quote(L"foo\\\\bar", L'"') == L"foo\\\\\\\\bar");
}
static void test_format() {
say(L"Testing formatting functions");
struct {
@ -1424,38 +1392,6 @@ static void test_indents() {
}
}
static void test_parse_util_cmdsubst_extent() {
const wchar_t *a = L"echo (echo (echo hi";
const wchar_t *begin = nullptr, *end = nullptr;
parse_util_cmdsubst_extent(a, 0, &begin, &end);
if (begin != a || end != begin + std::wcslen(begin)) {
err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__);
}
parse_util_cmdsubst_extent(a, 1, &begin, &end);
if (begin != a || end != begin + std::wcslen(begin)) {
err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__);
}
parse_util_cmdsubst_extent(a, 2, &begin, &end);
if (begin != a || end != begin + std::wcslen(begin)) {
err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__);
}
parse_util_cmdsubst_extent(a, 3, &begin, &end);
if (begin != a || end != begin + std::wcslen(begin)) {
err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__);
}
parse_util_cmdsubst_extent(a, 8, &begin, &end);
if (begin != a + const_strlen(L"echo (")) {
err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__);
}
parse_util_cmdsubst_extent(a, 17, &begin, &end);
if (begin != a + const_strlen(L"echo (echo (")) {
err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__);
}
}
static void test_const_strlen() {
do_test(const_strlen("") == 0);
do_test(const_strlen(L"") == 0);
@ -1599,7 +1535,6 @@ void test_dir_iter() {
static void test_utility_functions() {
say(L"Testing utility functions");
test_parse_util_cmdsubst_extent();
test_const_strlen();
test_const_strcmp();
test_is_sorted_by_name();
@ -6677,7 +6612,6 @@ static const test_t s_tests[]{
{TEST_GROUP("error_messages"), test_error_messages},
{TEST_GROUP("escape"), test_unescape_sane},
{TEST_GROUP("escape"), test_escape_crazy},
{TEST_GROUP("escape"), test_escape_quotes},
{TEST_GROUP("format"), test_format},
{TEST_GROUP("convert"), test_convert},
{TEST_GROUP("convert"), test_convert_private_use},