~sircmpwn/rc-devel

parse: Accept whitespace in variable sub-list access v2 APPLIED

Max Schillinger: 1
 parse: Accept whitespace in variable sub-list access

 2 files changed, 86 insertions(+), 58 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~sircmpwn/rc-devel/patches/46887/mbox | git am -3
Learn more about email & git

[PATCH v2] parse: Accept whitespace in variable sub-list access Export this patch

Accept expressions like these:

$var( 3 )
$var(2 - 4)
$var(2 -)
$var( $i )
$var($start - $end)
$var(- $end)

Add more tests.
---
Don't insert carets in parse_value for access lists. This allows a
simpler/shorter parse_access_range function.

 parse/value.ha | 103 +++++++++++++++++++++++++++----------------------
 test/access.rc |  41 ++++++++++++++------
 2 files changed, 86 insertions(+), 58 deletions(-)

diff --git a/parse/value.ha b/parse/value.ha
index a9746bf..eaca0bc 100644
--- a/parse/value.ha
+++ b/parse/value.ha
@@ -46,7 +46,7 @@ export fn parse_value_list(p: *parser, lf: bool) ([]*ast::value | error) = {
};

// Parses an [[ast::value]], returning true if there was trailing whitespace.
export fn parse_value(p: *parser) ((ast::value, bool) | error) = {
export fn parse_value(p: *parser, access: bool...) ((ast::value, bool) | error) = {
	const tok = want(p,
		ltok::DOLLAR,
		ltok::DOLLAR_POUND,
@@ -85,7 +85,8 @@ export fn parse_value(p: *parser) ((ast::value, bool) | error) = {
	};

	let had_ws = ws(p, false)?;
	if (!had_ws && peek(p,
	let is_access = len(access) > 0 && access[0] == true;
	if (!had_ws && !is_access && peek(p,
		ltok::DOLLAR,
		ltok::DOLLAR_POUND,
		ltok::DOLLAR_QUOTE,
@@ -146,17 +147,26 @@ fn parse_access(p: *parser) (ast::access | error) = {

// Parses an [[ast::access]] index or slice.
fn parse_access_range(p: *parser, access: *ast::access) (void | error) = {
	const (ix, _) = parse_value(p)?;
	access.atype = access_type::INDEX;
	let had_dash = false;

	ws(p, false)?;

	const (ix, _) = parse_value(p, true)?;
	match (ix) {
	case ast::access =>
		// p.e. $var($index)
		access.atype = access_type::INDEX;
		// p.e. $var($index) or $var($start-$end) or $var($start - $end)
		access.index = alloc(ix);
	case let arg: ast::argument =>
		if (strings::contains(arg, '-')) {
		// p.e. $var(2-4) or $var(-$end), everything starting with a number
		// or a dash
		if (arg == "-") {
			// p.e. $var(- 4) or $var(- $end)
			access.index = alloc("1": ast::string: ast::value);
			access.end = alloc("0": ast::string: ast::value);
			had_dash = true;
		} else if (strings::contains(arg, '-')) {
			// p.e. $var(2-4) or $var(2-) or $var(-4)
			access.atype = access_type::SLICE;

			let tokens = strings::tokenize(arg, "-");
			let start = strings::next_token(&tokens) as str;
			if (start == "") {
@@ -169,56 +179,55 @@ fn parse_access_range(p: *parser, access: *ast::access) (void | error) = {
				end = "0"; // "open end"
			};
			access.end = alloc(strings::dup(end): ast::string: ast::value);
			access.atype = access_type::SLICE;
			had_dash = true;
		} else {
			// p.e. $var(2)
			access.atype = access_type::INDEX;
			// p.e. $var(2) or $var(2 - 4)
			access.index = alloc(ix);
		};
	case let arg: ast::concat =>
		access.atype = access_type::SLICE;
		match (*arg.0) {
		case let arg0: ast::access =>
			// p.e. $var($start-) or $var($start-$end) or $var($start-3)
			access.index = alloc(ast::value_dup(arg.0));
		case let arg0: ast::argument =>
			// p.e. $var(-$end) or $var(2-$end)
			if (!strings::contains(arg0, '-')) {
				abort();
			};
			let tokens = strings::tokenize(arg0, "-");
			let start = strings::next_token(&tokens) as str;
			if (start == "") {
				start = "1"; // start with first value
	// error for all other types?
	};

	if (peek(p, ltok::RPAREN) is lex::token) {
		return;
	};

	access.atype = access_type::SLICE;

	// check for "-" and end index
	if (!had_dash) {
		const tok = try(p, ltok::ARGUMENT)?;
		if (tok is lex::token) {
			const arg = (tok as lex::token).1;
			if (arg != "-") {
				if (strings::sub(arg, 0, 1) != "-") {
					abort();
				};
				const rest = strings::sub(arg, 1, strings::end);
				access.end = alloc(strings::dup(rest): ast::string: ast::value);
			};
			access.index = alloc(strings::dup(start): ast::string: ast::value);
		};
	};

		match (*arg.1) {
		case let arg1: ast::access =>
			// p.e. $var(-$end) or $var(2-$end)
			access.end = alloc(ast::value_dup(arg.1));
		case let arg1: ast::argument =>
			// p.e. $var($start-) or $var($start-3)
			// if (!strings::contains(arg1, '-')) {
			// 	// error?
			// };
			let tokens = strings::tokenize(arg1, "-");
			strings::next_token(&tokens);
			let end = strings::remaining_tokens(&tokens);
	ws(p, false)?;

	if (peek(p, ltok::RPAREN)? is lex::token) {
		if (access.end == null) {
			access.end = alloc("0": ast::string: ast::value);
		};
	} else {
		const (end, _) = parse_value(p, true)?;
		match (end) {
		case let acc: ast::access =>
			access.end = alloc(end);
		case let end: ast::argument =>
			if (end == "") {
				end = "0"; // "open end"
			};
			access.end = alloc(strings::dup(end): ast::string: ast::value);
		case let arg1: ast::concat =>
			// p.e. $var($start-$end)
			// assuming *arg1.0 = "-", error if not?
			match (*arg1.1) {
			case let arg2: ast::access =>
				access.end = alloc(ast::value_dup(arg1.1));
			// error if other type?
			};
		case =>
			abort(); // error?
		};
	// error for all other types?
	};
};

diff --git a/test/access.rc b/test/access.rc
index 4c1bd96..a3bf407 100644
--- a/test/access.rc
+++ b/test/access.rc
@@ -17,6 +17,9 @@ begin "List index access"
		test $i -eq $x($i) || fail 'unexpected value'
		i=`{dc -e "$i 1+p"}
	}

	i=3
	test $i -eq $x( $i ) || fail 'unexpected value'
}
end

@@ -37,30 +40,46 @@ begin "Quoted access"
}
end

begin "List slice access"
begin "String slice access"
@{
	x=(1 2 3 4 5)
	y=$x(2-4)
	test $"y "=" "2 3 4" || fail 'expected $"x(2-4) to equal "2 3 4"'

	y=$x(2-)
	test $"y "=" "2 3 4 5" || fail 'expected $"x(2-) to equal "2 3 4 5"'

	y=$x(-4)
	test $"y "=" "1 2 3 4" || fail 'expected $"x(-4) to equal "1 2 3 4"'

	x="ABCDE"
	test $x(2-4) "=" "BCD" || fail 'expected $x(2-4) to equal "BCD"'
	test $x(2-) "=" "BCDE" || fail 'expected $x(2-) to equal "BCDE"'
	test $x(-4) "=" "ABCD" || fail 'expected $x(-4) to equal "ABCD"'
	test $x(2 - 4) "=" "BCD" || fail 'expected $x(2 - 4) to equal "BCD"'
	test $x(2 -) "=" "BCDE" || fail 'expected $x(2 -) to equal "BCDE"'
	test $x(2 - ) "=" "BCDE" || fail 'expected $x(2 - ) to equal "BCDE"'
	test $x(- 4) "=" "ABCD" || fail 'expected $x(- 4) to equal "ABCD"'
	test $x( - 4) "=" "ABCD" || fail 'expected $x( - 4) to equal "ABCD"'

	start=2
	end=4
	test $x($start-$end) "=" "BCD" || fail 'expected $x($start-$end) to equal "BCD"'
	test $x($start-) "=" "BCDE" || fail 'expected $x($start-) to equal "BCDE"'
	test $x(-$end) "=" "ABCD" || fail 'expected $x(-$end) to equal "ABCD"'
	test $x($start - $end) "=" "BCD" || fail 'expected $x($start - $end) to equal "BCD"'
	test $x($start -) "=" "BCDE" || fail 'expected $x($start -) to equal "BCDE"'
	test $x(- $end) "=" "ABCD" || fail 'expected $x(- $end) to equal "ABCD"'

	test $x($start-4) "=" "BCD" || fail 'expected $x($start-) to equal "BCD"'
	test $x(2-$end) "=" "BCD" || fail 'expected $x(-$end) to equal "BCD"'

	test $x($start - 4) "=" "BCD" || fail 'expected $x($start-) to equal "BCD"'
	test $x(2 - $end) "=" "BCD" || fail 'expected $x(-$end) to equal "BCD"'
}
end

begin "List slice access"
@{
	x=(1 2 3 4 5)
	y=$x(2-4)
	test $"y "=" "2 3 4" || fail 'expected $"x(2-4) to equal "2 3 4"'

	y=$x(2-)
	test $"y "=" "2 3 4 5" || fail 'expected $"x(2-) to equal "2 3 4 5"'

	y=$x(-4)
	test $"y "=" "1 2 3 4" || fail 'expected $"x(-4) to equal "1 2 3 4"'
}
end

-- 
2.42.1
Thanks!

To git@git.sr.ht:~sircmpwn/rc
   c8f393b..f408a95  master -> master