add cmd/printast and code to pretty-print ast
This commit is contained in:
parent
5d8bb54e0b
commit
2e57f7f6dc
35
ast/node.go
35
ast/node.go
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue