add cmd/printast and code to pretty-print ast

This commit is contained in:
Krzysztof Kowalczyk 2018-01-28 16:16:11 -08:00
parent 5d8bb54e0b
commit 2e57f7f6dc
3 changed files with 127 additions and 35 deletions

View File

@ -1,7 +1,6 @@
package ast
import (
"bytes"
"fmt"
)
@ -462,37 +461,3 @@ func WalkFunc(n Node, f NodeVisitorFunc) {
visitor := NodeVisitorFunc(f)
Walk(n, visitor)
}
func dump(ast Node) {
fmt.Println(dumpString(ast))
}
func getContent(node Node) []byte {
if c := node.AsContainer(); c != nil {
if c.Literal != nil {
return c.Literal
}
return c.Content
}
return nil
}
func dumpR(ast Node, depth int) string {
if ast == nil {
return ""
}
indent := bytes.Repeat([]byte("\t"), depth)
content := ast.AsContainer().Literal
if content == nil {
content = ast.AsContainer().Content
}
result := fmt.Sprintf("%s%T(%q)\n", indent, ast, content)
for _, child := range ast.GetChildren() {
result += dumpR(child, depth+1)
}
return result
}
func dumpString(ast Node) string {
return dumpR(ast, 0)
}

90
ast/print.go Normal file
View File

@ -0,0 +1,90 @@
package ast
import (
"bytes"
"fmt"
"io"
"strings"
)
// Print is for debugging. It prints a string representation of parsed
// markdown doc (result of parser.Parse()) to dst.
//
// To make output readable, it shortens text output.
func Print(dst io.Writer, doc Node) {
PrintWithPrefix(dst, doc, " ")
}
// PrintWithPrefix is like Pring but allows customizing prefix used for
// indentation. By default it's 2 spaces. You can change it to e.g. tab
// by passing "\t"
func PrintWithPrefix(w io.Writer, doc Node, prefix string) {
printRecur(w, doc, prefix, 0)
}
// ToString is like Dump but returns result as a string
func ToString(doc Node) string {
var buf bytes.Buffer
Print(&buf, doc)
return buf.String()
}
func contentToString(d1 []byte, d2 []byte) string {
if d1 != nil {
return string(d1)
}
if d2 != nil {
return string(d2)
}
return ""
}
func getContent(node Node) string {
if c := node.AsContainer(); c != nil {
return contentToString(c.Literal, c.Content)
}
leaf := node.AsLeaf()
return contentToString(leaf.Literal, leaf.Content)
}
func shortenString(s string, maxLen int) string {
// for cleaner, one-line ouput, replace some white-space chars
// with their escaped version
s = strings.Replace(s, "\n", `\n`, -1)
s = strings.Replace(s, "\r", `\r`, -1)
s = strings.Replace(s, "\t", `\t`, -1)
if maxLen < 0 {
return s
}
if len(s) < maxLen {
return s
}
// add "..." to indicate truncation
return s[:maxLen-3] + "..."
}
// get a short name of the type of v which excludes package name
// and strips "()" from the end
func getNodeType(node Node) string {
s := fmt.Sprintf("%T", node)
s = strings.TrimSuffix(s, "()")
if idx := strings.Index(s, "."); idx != -1 {
return s[idx+1:]
}
return s
}
func printRecur(w io.Writer, node Node, prefix string, depth int) {
if node == nil {
return
}
indent := strings.Repeat(prefix, depth)
io.WriteString(w, indent)
content := shortenString(getContent(node), 40)
typeName := getNodeType(node)
fmt.Fprintf(w, "%s%s '%s'\n", indent, typeName, content)
for _, child := range node.GetChildren() {
printRecur(w, child, prefix, depth+1)
}
}

37
cmd/printast/main.go Normal file
View File

@ -0,0 +1,37 @@
package main
import (
"fmt"
"io/ioutil"
"os"
"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/ast"
)
// This prints AST of parsed markdown document.
// Usage: printast <markdown-file>
func usageAndExit() {
fmt.Printf("Usage: printast <markdown-file>\n")
os.Exit(1)
}
func main() {
nFiles := len(os.Args) - 1
if nFiles < 1 {
usageAndExit()
}
for i := 0; i < nFiles; i++ {
fileName := os.Args[i+1]
d, err := ioutil.ReadFile(fileName)
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't open '%s', error: '%s'\n", fileName, err)
continue
}
doc := markdown.Parse(d, nil)
fmt.Printf("Ast of file '%s':\n", fileName)
ast.Print(os.Stdout, doc)
fmt.Print("\n")
}
}