Max Schillinger: 1 Implement the variable sub-list operator 4 files changed, 94 insertions(+), 4 deletions(-)
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 -3Learn more about email & git
--- 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: