From f2742f486dc142d60c83ea373eb1948a84c758c83970cdc0d97e96e46cd1c324 Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Sun, 13 Apr 2025 15:14:25 +0300 Subject: [PATCH] Tcl code dumper --- go/cmd/pp/main.go | 7 ++- go/cmd/pp/tcl.go | 146 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 go/cmd/pp/tcl.go diff --git a/go/cmd/pp/main.go b/go/cmd/pp/main.go index b26c41c..488215e 100644 --- a/go/cmd/pp/main.go +++ b/go/cmd/pp/main.go @@ -42,7 +42,6 @@ value of the signature's sid. } var ( - dumpJSON = flag.Bool("json", false, "Dump as JSON") MaxStrLen = flag.Uint("max-str-len", 64, "Maximal string length to print") MaxParseCycles = flag.Uint64("max-parse-cycles", 1<<63, @@ -50,6 +49,8 @@ var ( NoTotals = flag.Bool("no-totals", false, "Do not print how many bytes read") onlyV = flag.Bool("v", false, "Print only values") pthStr = flag.String("p", "", "Print only /that/keks/path") + dumpJSON = flag.Bool("json", false, "Dump as JSON") + dumpTcl = flag.Bool("tcl", false, "Dump as Tcl code") pth []string ) @@ -91,12 +92,14 @@ func main() { if err != nil { log.Fatal(err) } + } else if *dumpTcl { + tcl(ctx.Iter(), 1, false, false) } else { printer(ctx.Iter(), []string{}, 1, false, false) } off = ctx.Read } - if !*NoTotals && !*dumpJSON { + if !*NoTotals && !*dumpJSON && !*dumpTcl { fmt.Println(off, "bytes") } } diff --git a/go/cmd/pp/tcl.go b/go/cmd/pp/tcl.go new file mode 100644 index 0000000..1c6e622 --- /dev/null +++ b/go/cmd/pp/tcl.go @@ -0,0 +1,146 @@ +// pp -- KEKS pretty printer +// Copyright (C) 2024-2025 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +package main + +import ( + "encoding/hex" + "fmt" + "io" + "log" + "strconv" + "time" + + "go.cypherpunks.su/keks" + "go.cypherpunks.su/keks/be" + "go.cypherpunks.su/keks/types" + "go.cypherpunks.su/tai64n/v4" +) + +func tclTAI(t time.Time, ns int, as uint64) { + utc, isLeap := tai64n.Leapsecs.Sub(t) + if isLeap || utc.Unix() < 0 { + fmt.Printf("TAI64 %d %d %d", t.Unix(), ns, as) + } else { + fmt.Printf("TAI64 [UTCFromISO \"%s\"] %d %d", + utc.Format("2006-01-02 15:04:05"), ns, as) + } +} + +func tcl(iter *keks.Iterator, count int, inList, inMap bool) { + for range count { + if !iter.Next() { + panic("unexpected") + } + depth := iter.Depth + var key string + if inMap { + key = iter.Str() + if !iter.Next() { + panic("unexpected") + } + } + prindent(depth) + if inMap { + fmt.Print(key, " ") + } + if inList || inMap { + fmt.Print("{") + } + var listOrMap bool + switch iter.T { + case types.List: + listOrMap = true + fmt.Println("LIST {") + tcl(iter, iter.Len(), true, false) + prindent(depth) + fmt.Print("}") + case types.Map: + listOrMap = true + fmt.Println("MAP {") + tcl(iter, iter.Len(), false, true) + prindent(depth) + fmt.Print("}") + } + if !listOrMap { + switch iter.T { + case types.NIL: + fmt.Print("NIL") + case types.Bool: + if iter.Bool() { + fmt.Print("TRUE") + } else { + fmt.Print("FALSE") + } + case types.Hexlet: + h := iter.Hexlet() + fmt.Printf("HEXLET %s", h.UUID()) + case types.UInt: + fmt.Printf("INT %d", iter.UInt()) + case types.Int: + fmt.Printf("INT %d", iter.Int()) + case types.BigInt: + fmt.Printf("INT %d", iter.BigInt()) + case types.Blob: + blob := iter.Blob() + v, err := io.ReadAll(blob.Reader()) + if err != nil { + log.Fatal(err) + } + fmt.Printf("BLOB %d [binary decode hex \"%s\"]", + blob.ChunkLen, hex.EncodeToString(v)) + case types.TAI64: + t := iter.TAI64().Time() + tclTAI(t, 0, 0) + case types.TAI64N: + t := iter.TAI64N().Time() + tclTAI(t, t.Nanosecond(), 0) + case types.TAI64NA: + tai := iter.TAI64NA() + var n tai64n.TAI64N + copy(n[:], tai[:]) + t := n.Time() + tclTAI(t, t.Nanosecond(), be.Get(tai[12:])) + case types.Magic: + allPrintable := true + magic := iter.Magic() + for _, b := range []byte(magic) { + if !strconv.IsPrint(rune(b)) { + allPrintable = false + } + } + if allPrintable { + fmt.Printf("MAGIC \"%s\"", magic) + } else { + fmt.Printf("MAGIC [binary decode hex \"%s\"]", []byte(magic)) + } + case types.Bin: + fmt.Printf("BIN [binary decode hex \"%s\"]", + hex.EncodeToString(iter.Bin())) + case types.Str: + fmt.Printf("STR \"%s\"", iter.Str()) + case types.Raw: + fmt.Printf("RAW [binary decode hex \"%s\"]", + hex.EncodeToString(iter.Raw())) + default: + fmt.Print("???") + } + } + if inList || inMap { + fmt.Print("}") + } + fmt.Println("") + } +} -- 2.48.1