---
Make the upper value of a range inclusive.
ast/value.ha | 14 ++++++++++++++
interp/value.ha | 32 +++++++++++++++++++++++++++++++-
parse/value.ha | 28 ++++++++++++++++++++++++++--
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..bec0e7d 100644
--- a/interp/value.ha
+++ b/interp/value.ha
@@ -81,7 +81,37 @@ 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);
+ let end = value_int(&end)?;
+ if (end <= -1) {
+ return bounds;
+ };
+ let end = end: size;
+
+ match (var.value) {
+ case let x: str =>
+ if (ix >= len(x)) {
+ return bounds;
+ };
+ const end = if (end == 0) strings::end else end;
+ 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 == 0) 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..fb3eeed 100644
--- a/parse/value.ha
+++ b/parse/value.ha
@@ -133,12 +133,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..1b91e15 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 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"
+ 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"'
+
+ # y=$x(-4)
+ # test $y "=" "ABCD" || fail 'expected $x(-4) to equal "ABCD"'
+}
+end
finish
--
2.42.0
On Mon Oct 30, 2023 at 10:47 AM CET, Drew DeVault wrote:
> Another issue that came up after the fact:
>
> % x=(foo bar baz)
> % y=2
> % echo $x($y-)
> Error: Value is not a number
Oops! It looks like my new tests were too simple. I'll fix this.