diff options
Diffstat (limited to 'vendor/github.com/pelletier')
-rw-r--r-- | vendor/github.com/pelletier/go-toml/keysparsing.go | 62 | ||||
-rw-r--r-- | vendor/github.com/pelletier/go-toml/lexer.go | 59 | ||||
-rw-r--r-- | vendor/github.com/pelletier/go-toml/marshal.go | 40 | ||||
-rw-r--r-- | vendor/github.com/pelletier/go-toml/marshal_test.go | 78 | ||||
-rw-r--r-- | vendor/github.com/pelletier/go-toml/parser.go | 60 | ||||
-rw-r--r-- | vendor/github.com/pelletier/go-toml/parser_test.go | 57 | ||||
-rw-r--r-- | vendor/github.com/pelletier/go-toml/tomltree_create_test.go | 4 | ||||
-rw-r--r-- | vendor/github.com/pelletier/go-toml/tomltree_write.go | 37 |
8 files changed, 331 insertions, 66 deletions
diff --git a/vendor/github.com/pelletier/go-toml/keysparsing.go b/vendor/github.com/pelletier/go-toml/keysparsing.go index 9707c6884..0da938b03 100644 --- a/vendor/github.com/pelletier/go-toml/keysparsing.go +++ b/vendor/github.com/pelletier/go-toml/keysparsing.go @@ -23,19 +23,19 @@ var escapeSequenceMap = map[rune]rune{ type parseKeyState int const ( - BARE parseKeyState = iota - BASIC - LITERAL - ESC - UNICODE_4 - UNICODE_8 + bare parseKeyState = iota + basic + literal + esc + unicode4 + unicode8 ) func parseKey(key string) ([]string, error) { groups := []string{} var buffer bytes.Buffer var hex bytes.Buffer - state := BARE + state := bare wasInQuotes := false ignoreSpace := true expectDot := false @@ -48,66 +48,66 @@ func parseKey(key string) ([]string, error) { ignoreSpace = false } - if state == ESC { + if state == esc { if char == 'u' { - state = UNICODE_4 + state = unicode4 hex.Reset() } else if char == 'U' { - state = UNICODE_8 + state = unicode8 hex.Reset() } else if newChar, ok := escapeSequenceMap[char]; ok { buffer.WriteRune(newChar) - state = BASIC + state = basic } else { return nil, fmt.Errorf(`invalid escape sequence \%c`, char) } continue } - if state == UNICODE_4 || state == UNICODE_8 { + if state == unicode4 || state == unicode8 { if isHexDigit(char) { hex.WriteRune(char) } - if (state == UNICODE_4 && hex.Len() == 4) || (state == UNICODE_8 && hex.Len() == 8) { + if (state == unicode4 && hex.Len() == 4) || (state == unicode8 && hex.Len() == 8) { if value, err := strconv.ParseInt(hex.String(), 16, 32); err == nil { buffer.WriteRune(rune(value)) } else { return nil, err } - state = BASIC + state = basic } continue } switch char { case '\\': - if state == BASIC { - state = ESC - } else if state == LITERAL { + if state == basic { + state = esc + } else if state == literal { buffer.WriteRune(char) } case '\'': - if state == BARE { - state = LITERAL - } else if state == LITERAL { + if state == bare { + state = literal + } else if state == literal { groups = append(groups, buffer.String()) buffer.Reset() wasInQuotes = true - state = BARE + state = bare } expectDot = false case '"': - if state == BARE { - state = BASIC - } else if state == BASIC { + if state == bare { + state = basic + } else if state == basic { groups = append(groups, buffer.String()) buffer.Reset() - state = BARE + state = bare wasInQuotes = true } expectDot = false case '.': - if state != BARE { + if state != bare { buffer.WriteRune(char) } else { if !wasInQuotes { @@ -122,13 +122,13 @@ func parseKey(key string) ([]string, error) { wasInQuotes = false } case ' ': - if state == BASIC { + if state == basic { buffer.WriteRune(char) } else { expectDot = true } default: - if state == BARE { + if state == bare { if !isValidBareChar(char) { return nil, fmt.Errorf("invalid bare character: %c", char) } else if expectDot { @@ -140,10 +140,10 @@ func parseKey(key string) ([]string, error) { } } - // state must be BARE at the end - if state == ESC { + // state must be bare at the end + if state == esc { return nil, errors.New("unfinished escape sequence") - } else if state != BARE { + } else if state != bare { return nil, errors.New("mismatched quotes") } diff --git a/vendor/github.com/pelletier/go-toml/lexer.go b/vendor/github.com/pelletier/go-toml/lexer.go index 1b6647d66..209665676 100644 --- a/vendor/github.com/pelletier/go-toml/lexer.go +++ b/vendor/github.com/pelletier/go-toml/lexer.go @@ -575,8 +575,67 @@ func (l *tomlLexer) lexRightBracket() tomlLexStateFn { return l.lexRvalue } +type validRuneFn func(r rune) bool + +func isValidHexRune(r rune) bool { + return r >= 'a' && r <= 'f' || + r >= 'A' && r <= 'F' || + r >= '0' && r <= '9' || + r == '_' +} + +func isValidOctalRune(r rune) bool { + return r >= '0' && r <= '7' || r == '_' +} + +func isValidBinaryRune(r rune) bool { + return r == '0' || r == '1' || r == '_' +} + func (l *tomlLexer) lexNumber() tomlLexStateFn { r := l.peek() + + if r == '0' { + follow := l.peekString(2) + if len(follow) == 2 { + var isValidRune validRuneFn + switch follow[1] { + case 'x': + isValidRune = isValidHexRune + case 'o': + isValidRune = isValidOctalRune + case 'b': + isValidRune = isValidBinaryRune + default: + if follow[1] >= 'a' && follow[1] <= 'z' || follow[1] >= 'A' && follow[1] <= 'Z' { + return l.errorf("unknown number base: %s. possible options are x (hex) o (octal) b (binary)", string(follow[1])) + } + } + + if isValidRune != nil { + l.next() + l.next() + digitSeen := false + for { + next := l.peek() + if !isValidRune(next) { + break + } + digitSeen = true + l.next() + } + + if !digitSeen { + return l.errorf("number needs at least one digit") + } + + l.emit(tokenInteger) + + return l.lexRvalue + } + } + } + if r == '+' || r == '-' { l.next() } diff --git a/vendor/github.com/pelletier/go-toml/marshal.go b/vendor/github.com/pelletier/go-toml/marshal.go index 1bbdfa1d8..6280225e9 100644 --- a/vendor/github.com/pelletier/go-toml/marshal.go +++ b/vendor/github.com/pelletier/go-toml/marshal.go @@ -20,7 +20,8 @@ type tomlOpts struct { } type encOpts struct { - quoteMapKeys bool + quoteMapKeys bool + arraysOneElementPerLine bool } var encOptsDefaults = encOpts{ @@ -174,6 +175,25 @@ func (e *Encoder) QuoteMapKeys(v bool) *Encoder { return e } +// ArraysWithOneElementPerLine sets up the encoder to encode arrays +// with more than one element on multiple lines instead of one. +// +// For example: +// +// A = [1,2,3] +// +// Becomes +// +// A = [ +// 1, +// 2, +// 3 +// ] +func (e *Encoder) ArraysWithOneElementPerLine(v bool) *Encoder { + e.arraysOneElementPerLine = v + return e +} + func (e *Encoder) marshal(v interface{}) ([]byte, error) { mtype := reflect.TypeOf(v) if mtype.Kind() != reflect.Struct { @@ -187,8 +207,11 @@ func (e *Encoder) marshal(v interface{}) ([]byte, error) { if err != nil { return []byte{}, err } - s, err := t.ToTomlString() - return []byte(s), err + + var buf bytes.Buffer + _, err = t.writeTo(&buf, "", "", 0, e.arraysOneElementPerLine) + + return buf.Bytes(), err } // Convert given marshal struct or map value to toml tree @@ -218,7 +241,7 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er return nil, err } if e.quoteMapKeys { - keyStr, err := tomlValueStringRepresentation(key.String()) + keyStr, err := tomlValueStringRepresentation(key.String(), "", e.arraysOneElementPerLine) if err != nil { return nil, err } @@ -449,21 +472,18 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.V case *Tree: if isTree(mtype) { return d.valueFromTree(mtype, tval.(*Tree)) - } else { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval) } + return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval) case []*Tree: if isTreeSlice(mtype) { return d.valueFromTreeSlice(mtype, tval.([]*Tree)) - } else { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval) } + return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval) case []interface{}: if isOtherSlice(mtype) { return d.valueFromOtherSlice(mtype, tval.([]interface{})) - } else { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval) } + return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval) default: switch mtype.Kind() { case reflect.Bool: diff --git a/vendor/github.com/pelletier/go-toml/marshal_test.go b/vendor/github.com/pelletier/go-toml/marshal_test.go index e7d1d6e4d..291a80d2a 100644 --- a/vendor/github.com/pelletier/go-toml/marshal_test.go +++ b/vendor/github.com/pelletier/go-toml/marshal_test.go @@ -146,8 +146,8 @@ var docData = testDoc{ Second: &subdoc, }, SubDocList: []testSubDoc{ - testSubDoc{"List.First", 0}, - testSubDoc{"List.Second", 0}, + {"List.First", 0}, + {"List.Second", 0}, }, SubDocPtrs: []*testSubDoc{&subdoc}, } @@ -530,7 +530,7 @@ var strPtr = []*string{&str1, &str2} var strPtr2 = []*[]*string{&strPtr} var nestedTestData = nestedMarshalTestStruct{ - String: [][]string{[]string{"Five", "Six"}, []string{"One", "Two"}}, + String: [][]string{{"Five", "Six"}, {"One", "Two"}}, StringPtr: &strPtr2, } @@ -721,6 +721,7 @@ func TestEncodeQuotedMapKeys(t *testing.T) { t.Errorf("Bad maps marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } + func TestDecodeQuotedMapKeys(t *testing.T) { result := mapsTestStruct{} err := NewDecoder(bytes.NewBuffer(mapsTestToml)).Decode(&result) @@ -732,3 +733,74 @@ func TestDecodeQuotedMapKeys(t *testing.T) { t.Errorf("Bad maps unmarshal: expected %v, got %v", expected, result) } } + +type structArrayNoTag struct { + A struct { + B []int64 + C []int64 + } +} + +func TestMarshalArray(t *testing.T) { + expected := []byte(` +[A] + B = [1,2,3] + C = [1] +`) + + m := structArrayNoTag{ + A: struct { + B []int64 + C []int64 + }{ + B: []int64{1, 2, 3}, + C: []int64{1}, + }, + } + + b, err := Marshal(m) + + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(b, expected) { + t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b) + } +} + +func TestMarshalArrayOnePerLine(t *testing.T) { + expected := []byte(` +[A] + B = [ + 1, + 2, + 3 + ] + C = [1] +`) + + m := structArrayNoTag{ + A: struct { + B []int64 + C []int64 + }{ + B: []int64{1, 2, 3}, + C: []int64{1}, + }, + } + + var buf bytes.Buffer + encoder := NewEncoder(&buf).ArraysWithOneElementPerLine(true) + err := encoder.Encode(m) + + if err != nil { + t.Fatal(err) + } + + b := buf.Bytes() + + if !bytes.Equal(b, expected) { + t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b) + } +} diff --git a/vendor/github.com/pelletier/go-toml/parser.go b/vendor/github.com/pelletier/go-toml/parser.go index d492a1e6f..0f2ab7a33 100644 --- a/vendor/github.com/pelletier/go-toml/parser.go +++ b/vendor/github.com/pelletier/go-toml/parser.go @@ -212,13 +212,25 @@ func (p *tomlParser) parseAssign() tomlParserStateFn { } var numberUnderscoreInvalidRegexp *regexp.Regexp +var hexNumberUnderscoreInvalidRegexp *regexp.Regexp -func cleanupNumberToken(value string) (string, error) { +func numberContainsInvalidUnderscore(value string) error { if numberUnderscoreInvalidRegexp.MatchString(value) { - return "", errors.New("invalid use of _ in number") + return errors.New("invalid use of _ in number") } + return nil +} + +func hexNumberContainsInvalidUnderscore(value string) error { + if hexNumberUnderscoreInvalidRegexp.MatchString(value) { + return errors.New("invalid use of _ in hex number") + } + return nil +} + +func cleanupNumberToken(value string) string { cleanedVal := strings.Replace(value, "_", "", -1) - return cleanedVal, nil + return cleanedVal } func (p *tomlParser) parseRvalue() interface{} { @@ -235,20 +247,49 @@ func (p *tomlParser) parseRvalue() interface{} { case tokenFalse: return false case tokenInteger: - cleanedVal, err := cleanupNumberToken(tok.val) - if err != nil { - p.raiseError(tok, "%s", err) + cleanedVal := cleanupNumberToken(tok.val) + var err error + var val int64 + if len(cleanedVal) >= 3 && cleanedVal[0] == '0' { + switch cleanedVal[1] { + case 'x': + err = hexNumberContainsInvalidUnderscore(tok.val) + if err != nil { + p.raiseError(tok, "%s", err) + } + val, err = strconv.ParseInt(cleanedVal[2:], 16, 64) + case 'o': + err = numberContainsInvalidUnderscore(tok.val) + if err != nil { + p.raiseError(tok, "%s", err) + } + val, err = strconv.ParseInt(cleanedVal[2:], 8, 64) + case 'b': + err = numberContainsInvalidUnderscore(tok.val) + if err != nil { + p.raiseError(tok, "%s", err) + } + val, err = strconv.ParseInt(cleanedVal[2:], 2, 64) + default: + panic("invalid base") // the lexer should catch this first + } + } else { + err = numberContainsInvalidUnderscore(tok.val) + if err != nil { + p.raiseError(tok, "%s", err) + } + val, err = strconv.ParseInt(cleanedVal, 10, 64) } - val, err := strconv.ParseInt(cleanedVal, 10, 64) if err != nil { p.raiseError(tok, "%s", err) } return val case tokenFloat: - cleanedVal, err := cleanupNumberToken(tok.val) + err := numberContainsInvalidUnderscore(tok.val) if err != nil { p.raiseError(tok, "%s", err) } + cleanedVal := cleanupNumberToken(tok.val) val, err := strconv.ParseFloat(cleanedVal, 64) if err != nil { p.raiseError(tok, "%s", err) @@ -379,5 +420,6 @@ func parseToml(flow []token) *Tree { } func init() { - numberUnderscoreInvalidRegexp = regexp.MustCompile(`([^\d]_|_[^\d]|_$|^_)`) + numberUnderscoreInvalidRegexp = regexp.MustCompile(`([^\d]_|_[^\d])|_$|^_`) + hexNumberUnderscoreInvalidRegexp = regexp.MustCompile(`(^0x_)|([^\da-f]_|_[^\da-f])|_$|^_`) } diff --git a/vendor/github.com/pelletier/go-toml/parser_test.go b/vendor/github.com/pelletier/go-toml/parser_test.go index 029bc52c3..6c8eec6a3 100644 --- a/vendor/github.com/pelletier/go-toml/parser_test.go +++ b/vendor/github.com/pelletier/go-toml/parser_test.go @@ -82,6 +82,59 @@ func TestSimpleNumbers(t *testing.T) { }) } +func TestHexIntegers(t *testing.T) { + tree, err := Load(`a = 0xDEADBEEF`) + assertTree(t, tree, err, map[string]interface{}{"a": int64(3735928559)}) + + tree, err = Load(`a = 0xdeadbeef`) + assertTree(t, tree, err, map[string]interface{}{"a": int64(3735928559)}) + + tree, err = Load(`a = 0xdead_beef`) + assertTree(t, tree, err, map[string]interface{}{"a": int64(3735928559)}) + + _, err = Load(`a = 0x_1`) + if err.Error() != "(1, 5): invalid use of _ in hex number" { + t.Error("Bad error message:", err.Error()) + } +} + +func TestOctIntegers(t *testing.T) { + tree, err := Load(`a = 0o01234567`) + assertTree(t, tree, err, map[string]interface{}{"a": int64(342391)}) + + tree, err = Load(`a = 0o755`) + assertTree(t, tree, err, map[string]interface{}{"a": int64(493)}) + + _, err = Load(`a = 0o_1`) + if err.Error() != "(1, 5): invalid use of _ in number" { + t.Error("Bad error message:", err.Error()) + } +} + +func TestBinIntegers(t *testing.T) { + tree, err := Load(`a = 0b11010110`) + assertTree(t, tree, err, map[string]interface{}{"a": int64(214)}) + + _, err = Load(`a = 0b_1`) + if err.Error() != "(1, 5): invalid use of _ in number" { + t.Error("Bad error message:", err.Error()) + } +} + +func TestBadIntegerBase(t *testing.T) { + _, err := Load(`a = 0k1`) + if err.Error() != "(1, 5): unknown number base: k. possible options are x (hex) o (octal) b (binary)" { + t.Error("Error should have been returned.") + } +} + +func TestIntegerNoDigit(t *testing.T) { + _, err := Load(`a = 0b`) + if err.Error() != "(1, 5): number needs at least one digit" { + t.Error("Bad error message:", err.Error()) + } +} + func TestNumbersWithUnderscores(t *testing.T) { tree, err := Load("a = 1_000") assertTree(t, tree, err, map[string]interface{}{ @@ -642,7 +695,7 @@ func TestTomlValueStringRepresentation(t *testing.T) { {int64(12345), "12345"}, {uint64(50), "50"}, {float64(123.45), "123.45"}, - {bool(true), "true"}, + {true, "true"}, {"hello world", "\"hello world\""}, {"\b\t\n\f\r\"\\", "\"\\b\\t\\n\\f\\r\\\"\\\\\""}, {"\x05", "\"\\u0005\""}, @@ -652,7 +705,7 @@ func TestTomlValueStringRepresentation(t *testing.T) { "[\"gamma\",\"delta\"]"}, {nil, ""}, } { - result, err := tomlValueStringRepresentation(item.Value) + result, err := tomlValueStringRepresentation(item.Value, "", false) if err != nil { t.Errorf("Test %d - unexpected error: %s", idx, err) } diff --git a/vendor/github.com/pelletier/go-toml/tomltree_create_test.go b/vendor/github.com/pelletier/go-toml/tomltree_create_test.go index 1ca108a52..3465a1066 100644 --- a/vendor/github.com/pelletier/go-toml/tomltree_create_test.go +++ b/vendor/github.com/pelletier/go-toml/tomltree_create_test.go @@ -60,7 +60,7 @@ func TestTreeCreateToTree(t *testing.T) { }, "array": []string{"a", "b", "c"}, "array_uint": []uint{uint(1), uint(2)}, - "array_table": []map[string]interface{}{map[string]interface{}{"sub_map": 52}}, + "array_table": []map[string]interface{}{{"sub_map": 52}}, "array_times": []time.Time{time.Now(), time.Now()}, "map_times": map[string]time.Time{"now": time.Now()}, "custom_string_map_key": map[customString]interface{}{customString("custom"): "custom"}, @@ -97,7 +97,7 @@ func TestTreeCreateToTreeInvalidArrayMemberType(t *testing.T) { } func TestTreeCreateToTreeInvalidTableGroupType(t *testing.T) { - _, err := TreeFromMap(map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"hello": t}}}) + _, err := TreeFromMap(map[string]interface{}{"foo": []map[string]interface{}{{"hello": t}}}) expected := "cannot convert type *testing.T to Tree" if err.Error() != expected { t.Fatalf("expected error %s, got %s", expected, err.Error()) diff --git a/vendor/github.com/pelletier/go-toml/tomltree_write.go b/vendor/github.com/pelletier/go-toml/tomltree_write.go index 449f35a44..f5ef124f0 100644 --- a/vendor/github.com/pelletier/go-toml/tomltree_write.go +++ b/vendor/github.com/pelletier/go-toml/tomltree_write.go @@ -44,7 +44,7 @@ func encodeTomlString(value string) string { return b.String() } -func tomlValueStringRepresentation(v interface{}) (string, error) { +func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElementPerLine bool) (string, error) { switch value := v.(type) { case uint64: return strconv.FormatUint(value, 10), nil @@ -61,7 +61,7 @@ func tomlValueStringRepresentation(v interface{}) (string, error) { return "\"" + encodeTomlString(value) + "\"", nil case []byte: b, _ := v.([]byte) - return tomlValueStringRepresentation(string(b)) + return tomlValueStringRepresentation(string(b), indent, arraysOneElementPerLine) case bool: if value { return "true", nil @@ -76,21 +76,40 @@ func tomlValueStringRepresentation(v interface{}) (string, error) { rv := reflect.ValueOf(v) if rv.Kind() == reflect.Slice { - values := []string{} + var values []string for i := 0; i < rv.Len(); i++ { item := rv.Index(i).Interface() - itemRepr, err := tomlValueStringRepresentation(item) + itemRepr, err := tomlValueStringRepresentation(item, indent, arraysOneElementPerLine) if err != nil { return "", err } values = append(values, itemRepr) } + if arraysOneElementPerLine && len(values) > 1 { + stringBuffer := bytes.Buffer{} + valueIndent := indent + ` ` // TODO: move that to a shared encoder state + + stringBuffer.WriteString("[\n") + + for i, value := range values { + stringBuffer.WriteString(valueIndent) + stringBuffer.WriteString(value) + if i != len(values)-1 { + stringBuffer.WriteString(`,`) + } + stringBuffer.WriteString("\n") + } + + stringBuffer.WriteString(indent + "]") + + return stringBuffer.String(), nil + } return "[" + strings.Join(values, ",") + "]", nil } return "", fmt.Errorf("unsupported value type %T: %v", v, v) } -func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (int64, error) { +func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) { simpleValuesKeys := make([]string, 0) complexValuesKeys := make([]string, 0) @@ -113,7 +132,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) } - repr, err := tomlValueStringRepresentation(v.value) + repr, err := tomlValueStringRepresentation(v.value, indent, arraysOneElementPerLine) if err != nil { return bytesCount, err } @@ -178,7 +197,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( if err != nil { return bytesCount, err } - bytesCount, err = node.writeTo(w, indent+" ", combinedKey, bytesCount) + bytesCount, err = node.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine) if err != nil { return bytesCount, err } @@ -190,7 +209,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( return bytesCount, err } - bytesCount, err = subTree.writeTo(w, indent+" ", combinedKey, bytesCount) + bytesCount, err = subTree.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine) if err != nil { return bytesCount, err } @@ -216,7 +235,7 @@ func writeStrings(w io.Writer, s ...string) (int, error) { // WriteTo encode the Tree as Toml and writes it to the writer w. // Returns the number of bytes written in case of success, or an error if anything happened. func (t *Tree) WriteTo(w io.Writer) (int64, error) { - return t.writeTo(w, "", "", 0) + return t.writeTo(w, "", "", 0, false) } // ToTomlString generates a human-readable representation of the current tree. |