Added setting of index values
All checks were successful
Build / build (push) Successful in 2m20s

This commit is contained in:
Leon Mika 2025-06-14 02:52:03 +02:00
parent 2172fb86d8
commit e2f471c608
5 changed files with 121 additions and 3 deletions

View file

@ -539,6 +539,39 @@ func indexLookup(ctx context.Context, obj, elem Object, pos lexer.Position) (Obj
return nil, nil
}
func indexAssign(ctx context.Context, obj, elem, toVal Object, pos lexer.Position) (_ Object, err error) {
if obj == nil {
return nil, assignToNilIndex(pos)
}
switch v := obj.(type) {
case ModListable:
intIdx, ok := elem.(IntObject)
if !ok {
return nil, nil
}
if int(intIdx) >= 0 && int(intIdx) < v.Len() {
err = v.SetIndex(int(intIdx), toVal)
} else if int(intIdx) < 0 && int(intIdx) >= -v.Len() {
err = v.SetIndex(v.Len()+int(intIdx), toVal)
}
if err != nil {
return nil, err
}
return toVal, nil
case ModHashable:
strIdx, ok := elem.(StringObject)
if !ok {
return nil, nil
}
err = v.SetValue(string(strIdx), toVal)
if err != nil {
return nil, err
}
return toVal, nil
}
return nil, notModIndexableError(pos)
}
func indexBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err

View file

@ -14,6 +14,8 @@ var (
var (
tooManyFinallyBlocksError = newBadUsage("try needs at most 1 finally")
notIndexableError = newBadUsage("index only support on lists and hashes")
notModIndexableError = newBadUsage("list or hash cannot be modified")
assignToNilIndex = newBadUsage("assigning to nil index value")
)
type errorWithPos struct {

View file

@ -220,7 +220,51 @@ func (e evaluator) assignDot(ctx context.Context, ec *evalCtx, n astDot, toVal O
return e.assignArg(ctx, ec, n.Arg, toVal)
}
return nil, errors.New("TODO")
val, err := e.evalArgForDotAssign(ctx, ec, n.Arg)
if err != nil {
return nil, err
}
for i, dot := range n.DotSuffix {
isLast := i == len(n.DotSuffix)-1
var idx Object
if dot.KeyIdent != nil {
idx = StringObject(dot.KeyIdent.String())
} else {
idx, err = e.evalPipeline(ctx, ec, dot.Pipeline)
if err != nil {
return nil, err
}
}
if isLast {
val, err = indexAssign(ctx, val, idx, toVal, n.Pos)
} else {
val, err = indexLookup(ctx, val, idx, n.Pos)
}
if err != nil {
return nil, err
}
}
return val, nil
}
func (e evaluator) evalArgForDotAssign(ctx context.Context, ec *evalCtx, n astCmdArg) (Object, error) {
// Special case for dot assigns of 'a.b = c' where a is actually a var deref (i.e. $a)
// which is unnecessary for assignments. Likewise, having '$a.b = c' should be dissallowed
switch {
case n.Ident != nil:
if v, ok := ec.getVar(n.Ident.String()); ok {
return v, nil
}
return nil, nil
case n.Var != nil:
return nil, errors.New("cannot assign to a dereferenced variable")
}
return e.evalArg(ctx, ec, n)
}
func (e evaluator) evalArg(ctx context.Context, ec *evalCtx, n astCmdArg) (Object, error) {

View file

@ -116,6 +116,17 @@ func TestInst_Eval(t *testing.T) {
{desc: "parse comments 2", expr: parseComments2, wantObj: true, wantErr: nil},
{desc: "parse comments 3", expr: parseComments3, wantObj: true, wantErr: nil},
{desc: "parse comments 4", expr: parseComments4, wantObj: true, wantErr: nil},
// Assign dots
{desc: "assign dot 1", expr: `x = [1 2 3] ; x.(0) = 4 ; "$x"`, want: "[4 2 3]"},
{desc: "assign dot 2", expr: `x = [1 2 3] ; x.(1) = 5 ; "$x"`, want: "[1 5 3]"},
{desc: "assign dot 3", expr: `x = [1 2 3] ; x.(-1) = 6 ; "$x"`, want: "[1 2 6]"},
{desc: "assign dot 4", expr: `y = [a:1 b:2] ; y.a = "hello" ; "$y"`, want: `[a:hello b:2]`},
{desc: "assign dot 5", expr: `y = [a:1 b:2] ; y.b = "world" ; "$y"`, want: `[a:1 b:world]`},
{desc: "assign dot 6", expr: `y = [a:"b" b:2] ; y.($y.a) = "world" ; "$y"`, want: `[a:b b:world]`},
{desc: "assign dot 7", expr: `z = [a:[1 2] b:[3 3]] ; z.a.(1) = 3 ; "$z"`, want: `[a:[1 3] b:[3 3]]`},
{desc: "assign dot 8", expr: `z = [[1 2] [3 4]] ; z.(1).(0) = 5 ; "$z"`, want: `[[1 2] [5 4]]`},
{desc: "assign dot 7", expr: `z = [[a:1 b:2] [c:3 d:4]] ; z.(1).a = 5 ; "$z"`, want: `[[a:1 b:2] [a:5 c:3 d:4]]`},
}
for _, tt := range tests {

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"reflect"
"sort"
"strconv"
"strings"
"time"
@ -35,9 +36,13 @@ type ModListable interface {
// Insert adds a new item to the list. idx can be a positive
// number from 0 to len(), in which case the object will be inserted
// at that position. If idx is negative, then the item will be inserted
// at that position, shifting all other elements to the right.
// If idx is negative, then the item will be inserted
// at that position from the right.
Insert(idx int, obj Object) error
// SetIndex replaces the item at index position idx with obj.
SetIndex(idx int, obj Object) error
}
type Hashable interface {
@ -46,6 +51,11 @@ type Hashable interface {
Each(func(k string, v Object) error) error
}
type ModHashable interface {
Hashable
SetValue(k string, val Object) error
}
type ListObject []Object
func NewListObject() *ListObject {
@ -80,6 +90,11 @@ func (s *ListObject) Index(i int) Object {
return (*s)[i]
}
func (s *ListObject) SetIndex(i int, toVal Object) error {
(*s)[i] = toVal
return nil
}
type StringListObject []string
func (ss StringListObject) String() string {
@ -117,9 +132,17 @@ func (s HashObject) String() string {
return "[:]"
}
// Return the keys in sorted order
keys := make([]string, 0, len(s))
for k := range s {
keys = append(keys, k)
}
sort.Strings(keys)
sb := strings.Builder{}
sb.WriteString("[")
for k, v := range s {
for _, k := range keys {
v := s[k]
if sb.Len() != 1 {
sb.WriteString(" ")
}
@ -152,6 +175,11 @@ func (s HashObject) Each(fn func(k string, v Object) error) error {
return nil
}
func (s HashObject) SetValue(k string, val Object) error {
s[k] = val
return nil
}
type StringObject string
func (s StringObject) String() string {