~sircmpwn/rc-devel

Implement the variable sub-list operator v1 PROPOSED

Max Schillinger: 1
 Implement the variable sub-list operator

 4 files changed, 94 insertions(+), 4 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/46134/mbox | git am -3
Learn more about email & git

[PATCH] Implement the variable sub-list operator Export this patch

---
 ast/value.ha    | 14 ++++++++++++++
 interp/value.ha | 31 ++++++++++++++++++++++++++++++-
 parse/value.ha  | 29 +++++++++++++++++++++++++++--
 test/access.rc  | 24 +++++++++++++++++++++++-
 4 files changed, 94 insertions(+), 4 deletions(-)

diff --git a/ast/value.ha b/ast/value.ha
index 4f4ddc2..4c2f89f 100644
--- a/ast/value.ha
+++ b/ast/value.ha
@@ -24,6 +24,7 @@ export type access = struct {
	atype: access_type,
	target: str,
	index: nullable *value,
	end: nullable *value,
};

// A subscript value, e.g. `{echo hi}.
@@ -46,6 +47,13 @@ export fn value_finish(val: *value) void = {
			value_finish(val);
			free(val);
		};
		match (val.end) {
		case null =>
			yield;
		case let val: *value =>
			value_finish(val);
			free(val);
		};
	case let val: string =>
		free(val);
	case let val: argument =>
@@ -81,6 +89,12 @@ export fn value_dup(val: *value) value = {
			case let v: *value =>
				yield alloc(value_dup(v));
			},
			end = match (val.end) {
			case null =>
				yield null;
			case let v: *value =>
				yield alloc(value_dup(v));
			},
		};
	case let val: string =>
		return strings::dup(val): string;
diff --git a/interp/value.ha b/interp/value.ha
index 1ac8653..04299d5 100644
--- a/interp/value.ha
+++ b/interp/value.ha
@@ -81,7 +81,36 @@ export fn expand(state: *state, val: *ast::value, glob: bool) (value | error) =
				return value_dup(&x[ix]);
			};
		case access_type::SLICE =>
			abort(); // TODO
			const ix = expand(state, val.index as *ast::value, true)?;
			defer value_finish(&ix);
			const ix = value_int(&ix)?;
			if (ix <= 0) {
				return bounds;
			};
			const ix = ix: size - 1;

			const end = expand(state, val.end as *ast::value, true)?;
			defer value_finish(&end);
			const end = value_int(&end)?;
			if (end <= -1) {
				return bounds;
			};
			const end = end: size - 1;

			match (var.value) {
			case let x: str =>
				if (ix >= len(x)) {
					return bounds;
				};
				const sub = strings::sub(x, ix, end);
				return strings::dup(sub);
			case let x: []value =>
				if (ix >= len(x)) {
					return bounds;
				};
				let v: value = if (end == -1) x[ix..] else x[ix..end];
				return value_dup(&v);
			};
		};
	case let val: []*ast::value =>
		let list: []value = alloc([], len(val));
diff --git a/parse/value.ha b/parse/value.ha
index bff0372..0bc2d83 100644
--- a/parse/value.ha
+++ b/parse/value.ha
@@ -5,6 +5,7 @@ use lex::{ltok};
use io;
use strings;
use strconv;
use fmt;

// Parses a list of values.
export fn parse_value_list(p: *parser, lf: bool) ([]*ast::value | error) = {
@@ -133,12 +134,36 @@ fn parse_access(p: *parser) (ast::access | error) = {
		atype = atype,
		target = strings::dup(var.1),
		index = null,
		end = null,
	};

	if (atype == access_type::VAR && try(p, ltok::LPAREN)? is lex::token) {
		access.atype = access_type::INDEX;
		const (ix, _) = parse_value(p)?;
		access.index = alloc(ix);
		let arg: str = match (ix) {
		case let arg: ast::argument =>
			yield arg;
		case =>
			yield "";  // ix is not a range
		};
		if (strings::contains(arg, '-')) {
			access.atype = access_type::SLICE;

			let tokens = strings::tokenize(arg, "-");
			let start = strings::next_token(&tokens) as str;
			if (start == "") {
				start = "1"; // start with first value
			};
			access.index = alloc(strings::dup(start): ast::string: ast::value);

			let end = strings::remaining_tokens(&tokens);
			if (end == "") {
				end = "0"; // "open end"
			};
			access.end = alloc(strings::dup(end): ast::string: ast::value);
		} else {
			access.atype = access_type::INDEX;
			access.index = alloc(ix);
		};
		want(p, ltok::RPAREN)?;
	};

diff --git a/test/access.rc b/test/access.rc
index 5b24cea..1f58110 100644
--- a/test/access.rc
+++ b/test/access.rc
@@ -37,6 +37,28 @@ begin "Quoted access"
}
end

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

	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" || fail 'expected $"x(-4) to equal "1 2 3"'

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

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

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

finish
-- 
2.42.0
Great work! Just one nit: