[PATCH] Implement variable sub-list access using other variables
Export this patch
Example:
% x=(foo bar baz)
% y=2
% z=3
% echo $x($y-)
% echo $x($y-$z)
% echo $x(2-$z)
---
parse/value.ha | 73 +++++++++++++++++++++++++++++++++++++++++++-------
test/access.rc | 31 +++++++++++----------
2 files changed, 80 insertions(+), 24 deletions(-)
diff --git a/parse/value.ha b/parse/value.ha
index fb3eeed..e4d4fbd 100644
--- a/parse/value.ha
+++ b/parse/value.ha
@@ -137,14 +137,24 @@ fn parse_access(p: *parser) (ast::access | error) = {
};
if (atype == access_type::VAR && try(p, ltok::LPAREN)? is lex::token) {
- const (ix, _) = parse_value(p)?;
- let arg: str = match (ix) {
- case let arg: ast::argument =>
- yield arg;
- case =>
- yield ""; // ix is not a range
- };
+ parse_access_range(p, &access)?;
+ want(p, ltok::RPAREN)?;
+ };
+
+ return access;
+};
+
+// Parses an [[ast::access]] index or slice.
+fn parse_access_range(p: *parser, access: *ast::access) (void | error) = {
+ const (ix, _) = parse_value(p)?;
+ match (ix) {
+ case ast::access =>
+ // p.e. $var($index)
+ access.atype = access_type::INDEX;
+ access.index = alloc(ix);
+ case let arg: ast::argument =>
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, "-");
@@ -160,13 +170,56 @@ fn parse_access(p: *parser) (ast::access | error) = {
};
access.end = alloc(strings::dup(end): ast::string: ast::value);
} else {
+ // p.e. $var(2)
access.atype = access_type::INDEX;
access.index = alloc(ix);
};
- want(p, ltok::RPAREN)?;
- };
+ 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
+ };
+ access.index = alloc(strings::dup(start): ast::string: ast::value);
+ };
- return access;
+ 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);
+ 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?
+ };
+ };
+ // error for all other types?
+ };
};
// Parses an [[ast::subscript]] value.
diff --git a/test/access.rc b/test/access.rc
index 1b91e15..4c1bd96 100644
--- a/test/access.rc
+++ b/test/access.rc
@@ -39,25 +39,28 @@ 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"'
+ 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(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"'
+ y=$x(-4)
+ test $"y "=" "1 2 3 4" || fail 'expected $"x(-4) to equal "1 2 3 4"'
x="ABCDE"
- y=$x(2-4)
- test $y "=" "BCD" || fail 'expected $x(2-4) to equal "BCD"'
-
- # y=$x(2-)
- # test $y "=" "BCDE" || fail 'expected $x(2-) to equal "BCDE"'
+ 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"'
- # y=$x(-4)
- # test $y "=" "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-4) "=" "BCD" || fail 'expected $x($start-) to equal "BCD"'
+ test $x(2-$end) "=" "BCD" || fail 'expected $x(-$end) to equal "BCD"'
}
end
--
2.42.0