[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"'
The upper value should be inclusive, so this should print 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" || 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: