diff --git a/dict_test.go b/dict_test.go index bd93bab..f69d509 100644 --- a/dict_test.go +++ b/dict_test.go @@ -102,3 +102,49 @@ func TestDictParser(t *testing.T) { } t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed) } + +func TestDictToStringMultiLine(t *testing.T) { + var good bool + section := "dict-ToString-ML" + want := `{ + "first": 1 +}` + args := map[any]*term{ + "first": newLiteralTerm(NewValueToken(0, 0, SymInteger, "1", 1)), + } + dict := newDict(args) + got := dict.ToString(MultiLine) + // fmt.Printf("got=%q\n", got) + + if good = got == want; !good { + t.Errorf("ToString(MultiLine): got = %q, want %q", got, want) + } + + if good { + t.Logf("%s -- succeeded", section) + } else { + t.Logf("%s -- failed", section) + } +} + +func TestDictToString(t *testing.T) { + var good bool + section := "dict-ToString-SL" + want := `{"first": 1}` + args := map[any]*term{ + "first": newLiteralTerm(NewValueToken(0, 0, SymInteger, "1", 1)), + } + dict := newDict(args) + got := dict.ToString(0) + // fmt.Printf("got=%q\n", got) + + if good = got == want; !good { + t.Errorf("ToString(0): got = %q, want %q", got, want) + } + + if good { + t.Logf("%s -- succeeded", section) + } else { + t.Logf("%s -- failed", section) + } +} diff --git a/expr_test.go b/expr_test.go index fee2133..84536a1 100644 --- a/expr_test.go +++ b/expr_test.go @@ -22,7 +22,7 @@ func TestExpr(t *testing.T) { /* 3 */ {`f=openFile("test-file.txt"); line=readFile(f); closeFile(f); line`, "uno", nil}, /* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil}, /* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil}, - /* 10 */ {` + /* 6 */ {` ds={ "init":func(end){@end=end; @current=0 but true}, "current":func(){current}, diff --git a/operand-dict.go b/operand-dict.go index ba1f6f0..2ff04a4 100644 --- a/operand-dict.go +++ b/operand-dict.go @@ -12,18 +12,24 @@ import ( type DictType map[any]any -func newDict(dictAny []any) (dict *DictType) { +func newDict(dictAny map[any]*term) (dict *DictType) { + var d DictType if dictAny != nil { - d := make(DictType, len(dictAny)) + d = make(DictType, len(dictAny)) for i, item := range dictAny { d[i] = item } - dict = &d + } else { + d = make(DictType) } + dict = &d return } func (dict *DictType) toMultiLine(sb *strings.Builder, indent int) { + sb.WriteString(strings.Repeat("\t", indent)) + sb.WriteString("{\n") + first := true for name, value := range *dict { if first { @@ -35,13 +41,13 @@ func (dict *DictType) toMultiLine(sb *strings.Builder, indent int) { sb.WriteString(strings.Repeat("\t", indent+1)) if key, ok := name.(string); ok { - sb.WriteString(key) + sb.WriteString(string('"') + key + string('"')) } else { sb.WriteString(fmt.Sprintf("%v", name)) } sb.WriteString(": ") if f, ok := value.(Formatter); ok { - sb.WriteString(f.ToString(0)) + sb.WriteString(f.ToString(MultiLine)) } else if _, ok = value.(Functor); ok { sb.WriteString("func(){}") } else { @@ -49,18 +55,42 @@ func (dict *DictType) toMultiLine(sb *strings.Builder, indent int) { } } sb.WriteString(strings.Repeat("\t", indent)) - sb.WriteString("\n}\n") + sb.WriteString("\n}") } func (dict *DictType) ToString(opt FmtOpt) string { var sb strings.Builder if opt&MultiLine != 0 { dict.toMultiLine(&sb, 0) + } else { + sb.WriteByte('{') + first := true + for key, value := range *dict { + if first { + first = false + } else { + sb.WriteString(", ") + } + if s, ok := key.(string); ok { + sb.WriteString(string('"') + s + string('"')) + } else { + sb.WriteString(fmt.Sprintf("%v", key)) + } + sb.WriteString(": ") + if formatter, ok := value.(Formatter); ok { + sb.WriteString(formatter.ToString(opt)) + } else if t, ok := value.(*term); ok { + sb.WriteString(t.String()) + } else { + sb.WriteString(fmt.Sprintf("%#v", value)) + } + } + sb.WriteByte('}') } return sb.String() } -func (dict *DictType) hasKey(d map[any]any, target any) (ok bool) { +func (dict *DictType) hasKey(target any) (ok bool) { for key := range *dict { if ok = reflect.DeepEqual(key, target); ok { break @@ -69,6 +99,22 @@ func (dict *DictType) hasKey(d map[any]any, target any) (ok bool) { return } +func (dict *DictType) clone() (c *DictType) { + c = newDict(nil) + for k, v := range *dict { + (*c)[k] = v + } + return +} + +func (dict *DictType) merge(second *DictType) { + if second != nil { + for k, v := range *second { + (*dict)[k] = v + } + } +} + // -------- dict term func newDictTerm(args map[any]*term) *term { return &term{ @@ -84,7 +130,7 @@ func newDictTerm(args map[any]*term) *term { // -------- dict func func evalDict(ctx ExprContext, self *term) (v any, err error) { dict, _ := self.value().(map[any]*term) - items := make(map[any]any, len(dict)) + items := make(DictType, len(dict)) for key, tree := range dict { var param any if param, err = tree.compute(ctx); err != nil { @@ -93,7 +139,7 @@ func evalDict(ctx ExprContext, self *term) (v any, err error) { items[key] = param } if err == nil { - v = items + v = &items } return } diff --git a/operator-dot.go b/operator-dot.go index a13114f..48cbfa1 100644 --- a/operator-dot.go +++ b/operator-dot.go @@ -59,11 +59,11 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) { if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil { v = string(unboxedValue[index]) } - case map[any]any: + case *DictType: var ok bool var indexValue any if indexValue, err = indexTerm.compute(ctx); err == nil { - if v, ok = unboxedValue[indexValue]; !ok { + if v, ok = (*unboxedValue)[indexValue]; !ok { err = fmt.Errorf("key %v does not belong to the dictionary", rightValue) } } diff --git a/operator-length.go b/operator-length.go index 28bea6f..928e16f 100644 --- a/operator-length.go +++ b/operator-length.go @@ -30,8 +30,9 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) { s, _ := childValue.(string) v = int64(len(s)) } else if IsDict(childValue) { - m, _ := childValue.(map[any]any) - v = int64(len(m)) + // m, _ := childValue.(map[any]any) + m, _ := childValue.(*DictType) + v = int64(len(*m)) } else if it, ok := childValue.(Iterator); ok { if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) { count, _ := extIt.CallOperation(countName, nil) diff --git a/operator-sum.go b/operator-sum.go index 643fe38..85abace 100644 --- a/operator-sum.go +++ b/operator-sum.go @@ -62,12 +62,10 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) { v, err = sumAnyFract(leftValue, rightValue) } } else if IsDict(leftValue) && IsDict(rightValue) { - leftDict, _ := leftValue.(map[any]any) - rightDict, _ := rightValue.(map[any]any) - c := CloneMap(leftDict) - for key, value := range rightDict { - c[key] = value - } + leftDict, _ := leftValue.(*DictType) + rightDict, _ := rightValue.(*DictType) + c := leftDict.clone() + c.merge(rightDict) v = c } else { err = self.errIncompatibleTypes(leftValue, rightValue)