diff --git a/kern/clone-value.go b/kern/clone-value.go new file mode 100644 index 0000000..66d1ee5 --- /dev/null +++ b/kern/clone-value.go @@ -0,0 +1,28 @@ +// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com). +// All rights reserved. + +// clone-value.go +package kern + +func Clone(v any) (c any) { + if v == nil { + return + } + switch unboxed := v.(type) { + case int64: + c = unboxed + case float64: + c = unboxed + case string: + c = unboxed + case bool: + c = unboxed + case *ListType: + c = unboxed.Clone() + case *DictType: + c = unboxed.Clone() + default: + c = v + } + return +} diff --git a/kern/dict-type.go b/kern/dict-type.go index 43dfdae..342fc24 100644 --- a/kern/dict-type.go +++ b/kern/dict-type.go @@ -155,7 +155,7 @@ func (dict *DictType) GetItem(key any) (value any, exists bool) { func (dict *DictType) Clone() (c *DictType) { c = newDict(nil) for k, v := range *dict { - (*c)[k] = v + (*c)[k] = Clone(v) } return } diff --git a/kern/list-type.go b/kern/list-type.go index 0e2da80..7bf6c27 100644 --- a/kern/list-type.go +++ b/kern/list-type.go @@ -133,6 +133,15 @@ func (ls1 *ListType) Equals(ls2 ListType) (answer bool) { return } +func (ls1 *ListType) Clone() (ls2 *ListType) { + ls := make(ListType, len(*ls1)) + for i, item := range *ls1 { + ls[i] = Clone(item) + } + ls2 = &ls + return +} + func (dict *ListType) IndexDeepSameCmp(target any) (index int) { var eq bool var err error diff --git a/operator-assign.go b/operator-assign.go index 1b8ed14..0fa1a8b 100644 --- a/operator-assign.go +++ b/operator-assign.go @@ -22,6 +22,26 @@ func newAssignTerm(tk *scan.Token) (inst *scan.Term) { } } +func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { + v, err = generalEvalAssign(ctx, opTerm, false) + return +} + +func newDeepCopyAssignTerm(tk *scan.Token) (inst *scan.Term) { + return &scan.Term{ + Tk: *tk, + Children: make([]*scan.Term, 0, 2), + Position: scan.PosInfix, + Priority: scan.PriAssign, + EvalFunc: evalDeepCopyAssign, + } +} + +func evalDeepCopyAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { + v, err = generalEvalAssign(ctx, opTerm, true) + return +} + func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term, value any) (err error) { var collectionValue, keyListValue, keyValue any var keyList *kern.ListType @@ -57,16 +77,19 @@ func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *sca return } -func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any) (err error) { +func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any, deepCopy bool) (err error) { if leftTerm.Symbol() == scan.SymIndex { err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v) } else { + if deepCopy { + v = kern.Clone(v) + } ctx.UnsafeSetVar(leftTerm.Source(), v) } return } -func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { +func generalEvalAssign(ctx kern.ExprContext, opTerm *scan.Term, deepCopy bool) (v any, err error) { if err = opTerm.CheckOperands(); err != nil { return } @@ -93,10 +116,10 @@ func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { err = opTerm.Errorf("unknown function %s()", rightChild.Source()) } } else { - err = assignValue(ctx, leftTerm, v) + err = assignValue(ctx, leftTerm, v, deepCopy) } } else { - err = assignValue(ctx, leftTerm, v) + err = assignValue(ctx, leftTerm, v, deepCopy) } } if err != nil { @@ -203,7 +226,7 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source()) } if err == nil { - err = assignValue(ctx, leftTerm, v) + err = assignValue(ctx, leftTerm, v, false) } } } @@ -213,6 +236,7 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { // init func init() { scan.RegisterTermConstructor(scan.SymEqual, newAssignTerm) + scan.RegisterTermConstructor(scan.SymColonEqual, newDeepCopyAssignTerm) scan.RegisterTermConstructor(scan.SymPlusEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymMinusEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymStarEqual, newOpAssignTerm) diff --git a/scan/scanner.go b/scan/scanner.go index 9945f10..89d9d87 100644 --- a/scan/scanner.go +++ b/scan/scanner.go @@ -192,6 +192,8 @@ func (scanner *Scanner) fetchNextToken() (tk *Token) { case ':': if next, _ := scanner.peek(); next == ':' { tk = scanner.moveOn(SymDoubleColon, ch, next) + } else if next == '=' { + tk = scanner.moveOn(SymColonEqual, ch, next) } else { tk = scanner.MakeToken(SymColon, ch) } diff --git a/scan/symbol-map.go b/scan/symbol-map.go index b0e3efb..ff89794 100644 --- a/scan/symbol-map.go +++ b/scan/symbol-map.go @@ -64,47 +64,48 @@ func init() { SymAt: {"@", SymClassOperator, PosPrefix}, // 28: '@' SymUndescore: {"_", SymClassIdentifier, PosLeaf}, // 29: '_' SymEqual: {"=", SymClassOperator, PosInfix}, // 30: '=' - SymDoubleEqual: {"==", SymClassOperator, PosInfix}, // 31: '==' - SymLess: {"<", SymClassOperator, PosInfix}, // 32: '<' - SymLessOrEqual: {"<=", SymClassOperator, PosInfix}, // 33: '<=' - SymGreater: {">", SymClassOperator, PosInfix}, // 34: '>' - SymGreaterOrEqual: {">=", SymClassOperator, PosInfix}, // 35: '>=' - SymLessGreater: {"<>", SymClassOperator, PosInfix}, // 36: '<>' - SymNotEqual: {"!=", SymClassOperator, PosInfix}, // 37: '!=' - SymDollar: {"$", SymClassOperator, PosPrefix}, // 38: '$' - SymHash: {"#", SymClassOperator, PosPrefix}, // 39: '#' - SymOpenRound: {"(", SymClassParenthesis, PosPrefix}, // 40: '(' - SymClosedRound: {")", SymClassParenthesis, PosPostfix}, // 41: ')' - SymOpenSquare: {"[", SymClassParenthesis, PosPrefix}, // 42: '[' - SymClosedSquare: {"]", SymClassParenthesis, PosPostfix}, // 43: ']' - SymOpenBrace: {"{", SymClassParenthesis, PosPrefix}, // 44: '{' - SymClosedBrace: {"}", SymClassParenthesis, PosPostfix}, // 45: '}' - SymTilde: {"~", SymClassOperator, PosPrefix}, // 46: '~' - SymDoubleQuestion: {"??", SymClassOperator, PosInfix}, // 47: '??' - SymQuestionEqual: {"?=", SymClassOperator, PosInfix}, // 48: '?=' - SymQuestionExclam: {"?!", SymClassOperator, PosInfix}, // 49: '?!' - SymDoubleAt: {"@@", SymClassCommand, PosLeaf}, // 50: '@@' - SymDoubleColon: {"::", SymClassOperator, PosInfix}, // 51: '::' - SymDoubleGreater: {">>", SymClassOperator, PosInfix}, // 52: '>>' - SymDoubleLess: {"<<", SymClassOperator, PosInfix}, // 53: '<<' - SymCaret: {"^", SymClassOperator, PosInfix}, // 54: '^' - SymDollarRound: {"$(", SymClassOperator, PosPrefix}, // 55: '$(' - SymOpenClosedRound: {"()", SymClassOperator, PosPostfix}, // 56: '()' - SymDoubleDollar: {"$$", SymClassCommand, PosLeaf}, // 57: '$$' - SymDoubleDot: {"..", SymClassOperator, PosInfix}, // 58: '..' - SymTripleDot: {"...", SymClassOperator, PosPostfix}, // 59: '...' - SymStarEqual: {"*=", SymClassOperator, PosInfix}, // 60: '*=' - SymSlashEqual: {"/=", SymClassOperator, PosInfix}, // 61: '/=' - SymPercEqual: {"%=", SymClassOperator, PosInfix}, // 62: '%=' - SymDoubleLessEqual: {"<<=", SymClassOperator, PosInfix}, // 63: '<<=' - SymDoubleGreaterEqual: {">>=", SymClassOperator, PosInfix}, // 64: '>>=' - SymAmpersandEqual: {"&=", SymClassOperator, PosInfix}, // 65: '&=' - SymVertBarEqual: {"|=", SymClassOperator, PosInfix}, // 65: '|=' - SymCaretEqual: {"^=", SymClassOperator, PosInfix}, // 66: '^=' - SymPlusGreater: {"+>", SymClassOperator, PosInfix}, // 67: '+>' - SymLessPlus: {"<+", SymClassOperator, PosInfix}, // 68: '<+' - SymPreInc: {"++", SymClassOperator, PosPrefix}, // : '++' - SymPreDec: {"--", SymClassOperator, PosPrefix}, // : '--' + SymColonEqual: {":=", SymClassOperator, PosInfix}, // 31: ':=' + SymDoubleEqual: {"==", SymClassOperator, PosInfix}, // 32: '==' + SymLess: {"<", SymClassOperator, PosInfix}, // 33: '<' + SymLessOrEqual: {"<=", SymClassOperator, PosInfix}, // 34: '<=' + SymGreater: {">", SymClassOperator, PosInfix}, // 35: '>' + SymGreaterOrEqual: {">=", SymClassOperator, PosInfix}, // 36: '>=' + SymLessGreater: {"<>", SymClassOperator, PosInfix}, // 37: '<>' + SymNotEqual: {"!=", SymClassOperator, PosInfix}, // 38: '!=' + SymDollar: {"$", SymClassOperator, PosPrefix}, // 39: '$' + SymHash: {"#", SymClassOperator, PosPrefix}, // 40: '#' + SymOpenRound: {"(", SymClassParenthesis, PosPrefix}, // 41: '(' + SymClosedRound: {")", SymClassParenthesis, PosPostfix}, // 42: ')' + SymOpenSquare: {"[", SymClassParenthesis, PosPrefix}, // 43: '[' + SymClosedSquare: {"]", SymClassParenthesis, PosPostfix}, // 44: ']' + SymOpenBrace: {"{", SymClassParenthesis, PosPrefix}, // 45: '{' + SymClosedBrace: {"}", SymClassParenthesis, PosPostfix}, // 46: '}' + SymTilde: {"~", SymClassOperator, PosPrefix}, // 47: '~' + SymDoubleQuestion: {"??", SymClassOperator, PosInfix}, // 48: '??' + SymQuestionEqual: {"?=", SymClassOperator, PosInfix}, // 49: '?=' + SymQuestionExclam: {"?!", SymClassOperator, PosInfix}, // 50: '?!' + SymDoubleAt: {"@@", SymClassCommand, PosLeaf}, // 51: '@@' + SymDoubleColon: {"::", SymClassOperator, PosInfix}, // 52: '::' + SymDoubleGreater: {">>", SymClassOperator, PosInfix}, // 53: '>>' + SymDoubleLess: {"<<", SymClassOperator, PosInfix}, // 54: '<<' + SymCaret: {"^", SymClassOperator, PosInfix}, // 55: '^' + SymDollarRound: {"$(", SymClassOperator, PosPrefix}, // 56: '$(' + SymOpenClosedRound: {"()", SymClassOperator, PosPostfix}, // 57: '()' + SymDoubleDollar: {"$$", SymClassCommand, PosLeaf}, // 58: '$$' + SymDoubleDot: {"..", SymClassOperator, PosInfix}, // 59: '..' + SymTripleDot: {"...", SymClassOperator, PosPostfix}, // 60: '...' + SymStarEqual: {"*=", SymClassOperator, PosInfix}, // 61: '*=' + SymSlashEqual: {"/=", SymClassOperator, PosInfix}, // 62: '/=' + SymPercEqual: {"%=", SymClassOperator, PosInfix}, // 63: '%=' + SymDoubleLessEqual: {"<<=", SymClassOperator, PosInfix}, // 64: '<<=' + SymDoubleGreaterEqual: {">>=", SymClassOperator, PosInfix}, // 65: '>>=' + SymAmpersandEqual: {"&=", SymClassOperator, PosInfix}, // 66: '&=' + SymVertBarEqual: {"|=", SymClassOperator, PosInfix}, // 67: '|=' + SymCaretEqual: {"^=", SymClassOperator, PosInfix}, // 68: '^=' + SymPlusGreater: {"+>", SymClassOperator, PosInfix}, // 69: '+>' + SymLessPlus: {"<+", SymClassOperator, PosInfix}, // 70: '<+' + SymPreInc: {"++", SymClassOperator, PosPrefix}, // 71: '++' + SymPreDec: {"--", SymClassOperator, PosPrefix}, // 72: '--' // SymChangeSign // SymUnchangeSign // SymIdentifier diff --git a/scan/symbol.go b/scan/symbol.go index ee45309..18691b3 100644 --- a/scan/symbol.go +++ b/scan/symbol.go @@ -1,7 +1,7 @@ // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. -// Symbol.go +// symbol.go package scan type Symbol int16 @@ -39,71 +39,72 @@ const ( SymAt // 28: '@' SymUndescore // 29: '_' SymEqual // 30: '=' - SymDoubleEqual // 31: '==' - SymLess // 32: '<' - SymLessOrEqual // 33: '<=' - SymGreater // 34: '>' - SymGreaterOrEqual // 35: '>=' - SymLessGreater // 36: '<>' - SymNotEqual // 37: '!=' - SymDollar // 38: '$' - SymHash // 39: '#' - SymOpenRound // 40: '(' - SymClosedRound // 41: ')' - SymOpenSquare // 42: '[' - SymClosedSquare // 43: ']' - SymOpenBrace // 44: '{' - SymClosedBrace // 45: '}' - SymTilde // 46: '~' - SymDoubleQuestion // 47: '??' - SymQuestionEqual // 48: '?=' - SymQuestionExclam // 49: '?!' - SymDoubleAt // 50: '@@' - SymDoubleColon // 51: '::' - SymDoubleGreater // 52: '>>' - SymDoubleLess // 53: '<<' - SymCaret // 54: '^' - SymDollarRound // 55: '$(' - SymOpenClosedRound // 56: '()' - SymDoubleDollar // 57: '$$' - SymDoubleDot // 58: '..' - SymTripleDot // 59: '...' - SymStarEqual // 60: '*=' - SymSlashEqual // 61: '/=' - SymPercEqual // 62: '%=' - SymDoubleLessEqual // 63: '<<=' - SymDoubleGreaterEqual // 64: '>>=' - SymAmpersandEqual // 65: '&=' - SymVertBarEqual // 65: '|=' - SymCaretEqual // 66: '^=' - SymPlusGreater // 67: '+>' - SymLessPlus // 68: '<+' - SymChangeSign - SymUnchangeSign - SymDereference - SymPreInc - SymPreDec - SymIdentifier - SymBool - SymInteger - SymVariable - SymFloat - SymFraction - SymString - SymIterator - SymOr - SymAnd - SymNot - SymComment - SymFuncCall - SymFuncDef - SymList - SymDict - SymIndex - SymRange // [index : index] - SymExpression - SymSelector // ::= "?" {":" } ["::" ] - SymSelectorCase // ::= [] "{" "}" + SymColonEqual // 31: ':=' + SymDoubleEqual // 32: '==' + SymLess // 33: '<' + SymLessOrEqual // 34: '<=' + SymGreater // 35: '>' + SymGreaterOrEqual // 36: '>=' + SymLessGreater // 37: '<>' + SymNotEqual // 38: '!=' + SymDollar // 39: '$' + SymHash // 40: '#' + SymOpenRound // 41: '(' + SymClosedRound // 42: ')' + SymOpenSquare // 43: '[' + SymClosedSquare // 44: ']' + SymOpenBrace // 45: '{' + SymClosedBrace // 46: '}' + SymTilde // 47: '~' + SymDoubleQuestion // 48: '??' + SymQuestionEqual // 49: '?=' + SymQuestionExclam // 50: '?!' + SymDoubleAt // 51: '@@' + SymDoubleColon // 52: '::' + SymDoubleGreater // 53: '>>' + SymDoubleLess // 54: '<<' + SymCaret // 55: '^' + SymDollarRound // 56: '$(' + SymOpenClosedRound // 57: '()' + SymDoubleDollar // 58: '$$' + SymDoubleDot // 59: '..' + SymTripleDot // 60: '...' + SymStarEqual // 61: '*=' + SymSlashEqual // 62: '/=' + SymPercEqual // 63: '%=' + SymDoubleLessEqual // 64: '<<=' + SymDoubleGreaterEqual // 65: '>>=' + SymAmpersandEqual // 66: '&=' + SymVertBarEqual // 67: '|=' + SymCaretEqual // 68: '^=' + SymPlusGreater // 69: '+>' + SymLessPlus // 70: '<+' + SymChangeSign // 71: '-' + SymUnchangeSign // 72: '+'' + SymDereference // 73: '*' + SymPreInc // 74: '++' + SymPreDec // 75: '--' + SymIdentifier // 76: identifier + SymBool // 77: boolean + SymInteger // 78: integer + SymVariable // 79: variable + SymFloat // 80: float + SymFraction // 81: fraction + SymString // 82: string + SymIterator // 83: iterator + SymOr // 84: 'or' + SymAnd // 85: 'and' + SymNot // 86: 'not' + SymComment // 87: comment + SymFuncCall // 88: function call + SymFuncDef // 89: function definition + SymList // 90: list + SymDict // 91: dict + SymIndex // 92: index + SymRange // 93: range [index : index] + SymExpression // 94: expression + SymSelector // 95: selector ::= "?" {":" } ["::" ] + SymSelectorCase // 96: ::= [] "{" "}" // SymOpenComment // 0: '/*' // SymClosedComment // 0: '*/' // SymOneLineComment // 0: '//' diff --git a/t_dict_test.go b/t_dict_test.go index aad085a..62d2f56 100644 --- a/t_dict_test.go +++ b/t_dict_test.go @@ -7,7 +7,6 @@ package expr import ( "errors" - "strings" "testing" "git.portale-stac.it/go-pkg/expr/kern" @@ -17,105 +16,31 @@ import ( func TestDictParser(t *testing.T) { section := "Dict" - type inputType struct { - source string - wantResult any - wantErr error - } - inputs := []inputType{ - /* 1 */ {`{}`, map[any]any{}, nil}, + /* 1 */ {`{}`, kern.NewDict(nil), nil}, /* 2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")}, - /* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil}, + /* 3 */ {`{1:"one",2:"two",3:"three"}`, kern.NewDict(map[any]any{int64(1): "one", int64(2): "two", int64(3): "three"}), nil}, /* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil}, /* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil}, - /* 6 */ {`{1:"one"} + {2:"two"}`, map[any]any{1: "one", 2: "two"}, nil}, + /* 6 */ {`{1:"one"} + {2:"two"}`, kern.NewDict(map[any]any{int64(1): "one", int64(2): "two"}), nil}, /* 7 */ {`2 in {1:"one", 2:"two"}`, true, nil}, - /* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, map[any]any{"a": 9, "b": 2}, nil}, - /* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil}, + /* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, kern.NewDict(map[any]any{"a": int64(9), "b": int64(2)}), nil}, + /* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, kern.NewDict(map[any]any{"z": int64(9), "a": int64(1), "b": int64(2)}), nil}, /* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)}, /* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil}, /* 12 */ {`m={ "a":1, //"b":2, "c":3 - }`, map[any]any{"a": 1, "c": 3}, nil}, + }`, kern.NewDict(map[any]any{"a": int64(1), "c": int64(3)}), nil}, /* 13 */ {`D={"a":1, "b":2}; D."a"`, int64(1), nil}, /* 14 */ {`D={"a":1, "b":2}; D.a`, int64(1), nil}, /* 15 */ {`D={1:"a", 2:"b", 3:"c"}; D.(1+2)`, "c", nil}, + /* 16 */ {`D={"a":1, "b":2}; D.a`, int64(1), nil}, } - succeeded := 0 - failed := 0 - - // inputs1 := []inputType{ - // /* 7 */ {`add([1,4,3,2])`, int64(10), nil}, - // } - - for i, input := range inputs { - var expr *scan.Ast - var gotResult any - var gotErr error - - ctx := NewSimpleStoreWithoutGlobalContext() - ctx.SetVar("var1", int64(123)) - ctx.SetVar("var2", "abc") - ImportMathFuncs(ctx) - parser := NewParser() - - logTest(t, i+1, "Dict", input.source, input.wantResult, input.wantErr) - - r := strings.NewReader(input.source) - scanner := scan.NewScanner(r, scan.DefaultTranslations()) - - good := true - if expr, gotErr = parser.Parse(scanner); gotErr == nil { - gotResult, gotErr = expr.Eval(ctx) - } - - if (gotResult == nil && input.wantResult != nil) || (gotResult != nil && input.wantResult == nil) { - t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) - good = false - } - - if gotList, okGot := gotResult.([]any); okGot { - if wantList, okWant := input.wantResult.([]any); okWant { - if (gotList == nil && wantList != nil) || (gotList != nil && wantList == nil) { - t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) - good = false - } else { - equal := len(gotList) == len(wantList) - if equal { - for i, gotItem := range gotList { - wantItem := wantList[i] - equal = gotItem == wantItem - if !equal { - break - } - } - } - if !equal { - t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) - good = false - } - } - } - } - - if gotErr != input.wantErr { - if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) { - t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr) - good = false - } - } - - if good { - succeeded++ - } else { - failed++ - } - } - t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed) + runTestSuiteSpec(t, section, inputs, 16) + // runTestSuite(t, section, inputs) } func TestDictToStringMultiLine(t *testing.T) { diff --git a/t_operator_test.go b/t_operator_test.go index aed8a38..74754ac 100644 --- a/t_operator_test.go +++ b/t_operator_test.go @@ -54,7 +54,7 @@ func TestOperatorInsert(t *testing.T) { /* 6 */ {`[] >> ["a", "b"]`, kern.NewListA("a", "b"), nil}, /* 7 */ {`["a", "b"] << $([1,2,3])`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil}, /* 8 */ {`L=["a", "b"]; L << $([1,2,3])`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil}, - /* 9 */ {`L=["a", "b"]; L << $([1,2,3]); L`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil}, + /* 9 */ {`L=["a", "b"]; L << $([1,2,3]); L`, kern.NewListA("a", "b"), nil}, /* 10 */ {`L << $([1,2,3])`, nil, `undefined variable or function "L"`}, } @@ -62,6 +62,19 @@ func TestOperatorInsert(t *testing.T) { runTestSuite(t, section, inputs) } +func TestOperatorDeepAssign(t *testing.T) { + section := "Operator-DeepAssign" + inputs := []inputType{ + /* 1 */ {`DD={"uno": 1, "due": 2}; D=DD; D["uno"]=11; DD`, kern.NewDict(map[any]any{"uno": int64(11), "due": int64(2)}), nil}, + /* 2 */ {`DD={"uno": 1, "due": 2}; D:=DD; D["uno"]=11; DD`, kern.NewDict(map[any]any{"uno": int64(1), "due": int64(2)}), nil}, + /* 3 */ {`LL=["a", "b"]; L=LL; L[0]="x"; LL`, kern.NewListA("x", "b"), nil}, + /* 4 */ {`LL=["a", "b"]; L:=LL; L[0]="x"; LL`, kern.NewListA("a", "b"), nil}, + } + + // runTestSuiteSpec(t, section, inputs, 4) + runTestSuite(t, section, inputs) +} + func TestOperatorDigest(t *testing.T) { section := "Operator-Digest" inputs := []inputType{