diff --git a/dict-type.go b/dict-type.go index d935e03..b317903 100644 --- a/dict-type.go +++ b/dict-type.go @@ -32,42 +32,52 @@ func newDict(dictAny map[any]*term) (dict *DictType) { return } -func (dict *DictType) toMultiLine(sb *strings.Builder, indent int) { - sb.WriteString(strings.Repeat("\t", indent)) - sb.WriteString("{\n") +func (dict *DictType) toMultiLine(sb *strings.Builder, opt FmtOpt) { + indent := GetFormatIndent(opt) + flags := GetFormatFlags(opt) + //sb.WriteString(strings.Repeat(" ", indent)) + sb.WriteByte('{') - first := true - for name, value := range *dict { - if first { - first = false - } else { - sb.WriteByte(',') - sb.WriteByte('\n') - } + if len(*dict) > 0 { + innerOpt := MakeFormatOptions(flags, indent+1) + nest := strings.Repeat(" ", indent+1) + sb.WriteByte('\n') - sb.WriteString(strings.Repeat("\t", indent+1)) - if key, ok := name.(string); ok { - sb.WriteString(string('"') + key + string('"')) - } else { - sb.WriteString(fmt.Sprintf("%v", name)) - } - sb.WriteString(": ") - if f, ok := value.(Formatter); ok { - sb.WriteString(f.ToString(MultiLine)) - } else if _, ok = value.(Functor); ok { - sb.WriteString("func(){}") - } else { - sb.WriteString(fmt.Sprintf("%v", value)) + first := true + for name, value := range *dict { + if first { + first = false + } else { + sb.WriteByte(',') + sb.WriteByte('\n') + } + + sb.WriteString(nest) + if key, ok := name.(string); ok { + sb.WriteString(string('"') + key + string('"')) + } else { + sb.WriteString(fmt.Sprintf("%v", name)) + } + sb.WriteString(": ") + if f, ok := value.(Formatter); ok { + sb.WriteString(f.ToString(innerOpt)) + } else if _, ok = value.(Functor); ok { + sb.WriteString("func(){}") + } else { + sb.WriteString(fmt.Sprintf("%v", value)) + } } + sb.WriteByte('\n') + sb.WriteString(strings.Repeat(" ", indent)) } - sb.WriteString(strings.Repeat("\t", indent)) - sb.WriteString("\n}") + sb.WriteString("}") } func (dict *DictType) ToString(opt FmtOpt) string { var sb strings.Builder - if opt&MultiLine != 0 { - dict.toMultiLine(&sb, 0) + flags := GetFormatFlags(opt) + if flags&MultiLine != 0 { + dict.toMultiLine(&sb, opt) } else { sb.WriteByte('{') first := true @@ -133,3 +143,9 @@ func (dict *DictType) setItem(key any, value any) (err error) { (*dict)[key] = value return } + +//////////////// + +type DictFormat interface { + ToDict() *DictType +} diff --git a/formatter.go b/formatter.go index 120cd86..e188941 100644 --- a/formatter.go +++ b/formatter.go @@ -6,7 +6,7 @@ package expr import "fmt" -type FmtOpt uint16 +type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number const ( TTY FmtOpt = 1 << iota @@ -30,6 +30,18 @@ func TruncateString(s string) (trunc string) { return } +func MakeFormatOptions(flags FmtOpt, indent int) FmtOpt { + return FmtOpt(indent<<16) | flags +} + +func GetFormatFlags(opt FmtOpt) FmtOpt { + return opt & 0xFFFF +} + +func GetFormatIndent(opt FmtOpt) int { + return int(opt >> 16) +} + type Formatter interface { ToString(options FmtOpt) string } diff --git a/function.go b/function.go index 4055e42..c5bb452 100644 --- a/function.go +++ b/function.go @@ -21,7 +21,7 @@ func (functor *baseFunctor) ToString(opt FmtOpt) (s string) { if functor.info != nil { s = functor.info.ToString(opt) } else { - s = "func() {}" + s = "func() {}" } return s } @@ -180,7 +180,7 @@ func (info *funcInfo) ToString(opt FmtOpt) string { } else { sb.WriteString(TypeAny) } - sb.WriteString(" {}") + sb.WriteString(" {}") return sb.String() } diff --git a/list-type.go b/list-type.go index 72126cd..f675d6b 100644 --- a/list-type.go +++ b/list-type.go @@ -52,16 +52,24 @@ func ListFromStrings(stringList []string) (list *ListType) { } func (ls *ListType) ToString(opt FmtOpt) (s string) { + indent := GetFormatIndent(opt) + flags := GetFormatFlags(opt) + var sb strings.Builder sb.WriteByte('[') if len(*ls) > 0 { - if opt&MultiLine != 0 { - sb.WriteString("\n ") + innerOpt := MakeFormatOptions(flags, indent+1) + nest := strings.Repeat(" ", indent+1) + + if flags&MultiLine != 0 { + sb.WriteByte('\n') + sb.WriteString(nest) } for i, item := range []any(*ls) { if i > 0 { - if opt&MultiLine != 0 { - sb.WriteString(",\n ") + if flags&MultiLine != 0 { + sb.WriteString(",\n") + sb.WriteString(nest) } else { sb.WriteString(", ") } @@ -70,17 +78,20 @@ func (ls *ListType) ToString(opt FmtOpt) (s string) { sb.WriteByte('"') sb.WriteString(s) sb.WriteByte('"') + } else if formatter, ok := item.(Formatter); ok { + sb.WriteString(formatter.ToString(innerOpt)) } else { sb.WriteString(fmt.Sprintf("%v", item)) } } - if opt&MultiLine != 0 { + if flags&MultiLine != 0 { sb.WriteByte('\n') + sb.WriteString(strings.Repeat(" ", indent)) } } sb.WriteByte(']') s = sb.String() - if opt&Truncate != 0 && len(s) > TruncateSize { + if flags&Truncate != 0 && len(s) > TruncateSize { s = TruncateString(s) } return diff --git a/operator-context.go b/operator-context.go index 2f7f8f4..ab126b4 100644 --- a/operator-context.go +++ b/operator-context.go @@ -31,7 +31,9 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) { } if sourceCtx != nil { - if formatter, ok := sourceCtx.(Formatter); ok { + if formatter, ok := sourceCtx.(DictFormat); ok { + v = formatter.ToDict() + } else if formatter, ok := sourceCtx.(Formatter); ok { v = formatter.ToString(0) } else { // keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' }) diff --git a/simple-store.go b/simple-store.go index 44fcca6..c460970 100644 --- a/simple-store.go +++ b/simple-store.go @@ -7,7 +7,7 @@ package expr import ( "fmt" "slices" - "strings" +// "strings" ) type SimpleStore struct { @@ -50,7 +50,7 @@ func (ctx *SimpleStore) Merge(src ExprContext) { ctx.funcStore[name], _ = src.GetFuncInfo(name) } } - +/* func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) { sb.WriteString("vars: {\n") first := true @@ -116,6 +116,49 @@ func (ctx *SimpleStore) ToString(opt FmtOpt) string { funcsCtxToBuilder(&sb, ctx, 0) return sb.String() } +*/ + +func (ctx *SimpleStore) ToString(opt FmtOpt) string { + dict := ctx.ToDict() + return dict.ToString(opt) +} + +func (ctx *SimpleStore) varsToDict(dict *DictType) *DictType { + names := ctx.EnumVars(nil) + slices.Sort(names) + for _, name := range ctx.EnumVars(nil) { + value, _ := ctx.GetVar(name) + if f, ok := value.(Formatter); ok { + (*dict)[name] = f.ToString(0) + } else if _, ok = value.(Functor); ok { + (*dict)[name] = "func(){}" + } else { + (*dict)[name] = fmt.Sprintf("%v", value) + } + } + return dict +} + +func (ctx *SimpleStore) funcsToDict(dict *DictType) *DictType { + names := ctx.EnumFuncs(func(name string) bool { return true }) + slices.Sort(names) + for _, name := range names { + value, _ := ctx.GetFuncInfo(name) + if formatter, ok := value.(Formatter); ok { + (*dict)[name] = formatter.ToString(0) + } else { + (*dict)[name] = fmt.Sprintf("%v", value) + } + } + return dict +} + +func (ctx *SimpleStore) ToDict() (dict *DictType) { + dict = MakeDict() + (*dict)["variables"] = ctx.varsToDict(MakeDict()) + (*dict)["functions"] = ctx.funcsToDict(MakeDict()) + return +} func (ctx *SimpleStore) GetVar(varName string) (v any, exists bool) { v, exists = ctx.varStore[varName] diff --git a/t_dict_test.go b/t_dict_test.go index e2be1e8..e878d44 100644 --- a/t_dict_test.go +++ b/t_dict_test.go @@ -110,7 +110,7 @@ func TestDictToStringMultiLine(t *testing.T) { var good bool section := "dict-ToString-ML" want := `{ - "first": 1 + "first": 1 }` args := map[any]*term{ "first": newLiteralTerm(NewValueToken(0, 0, SymInteger, "1", 1)), diff --git a/t_funcs_test.go b/t_funcs_test.go index 526c52c..af7da0c 100644 --- a/t_funcs_test.go +++ b/t_funcs_test.go @@ -44,7 +44,7 @@ func dummy(ctx ExprContext, name string, args []any) (result any, err error) { func TestFunctionToStringSimple(t *testing.T) { source := NewGolangFunctor(dummy) - want := "func() {}" + want := "func() {}" got := source.ToString(0) if got != want { t.Errorf(`(func() -> result = %v [%T], want = %v [%T]`, got, got, want, want)