printer: implement a working version

This commit is contained in:
Fatih Arslan 2015-10-25 16:08:09 +03:00
parent 5918e3592b
commit 958093df59
3 changed files with 71 additions and 57 deletions

View File

@ -3,30 +3,38 @@ package printer
import (
"bytes"
"fmt"
"io"
"github.com/fatih/hcl/ast"
)
const (
blank = byte(' ')
newline = byte('\n')
tab = byte('\t')
)
func (p *printer) printNode(n ast.Node) []byte {
var buf bytes.Buffer
switch t := n.(type) {
case *ast.ObjectList:
fmt.Println("printing objectList", t)
for _, item := range t.Items {
for i, item := range t.Items {
buf.Write(p.printObjectItem(item))
if i != len(t.Items)-1 {
buf.WriteByte(newline)
}
}
case *ast.ObjectKey:
fmt.Println("printing objectKey", t)
buf.WriteString(t.Token.Text)
case *ast.ObjectItem:
fmt.Println("printing objectItem", t)
buf.Write(p.printObjectItem(t))
case *ast.LiteralType:
buf.Write(p.printLiteral(t))
buf.WriteString(t.Token.Text)
case *ast.ListType:
buf.Write(p.printList(t))
case *ast.ObjectType:
fmt.Println("printing ObjectType", t)
buf.Write(p.printObjectType(t))
default:
fmt.Printf(" unknown type: %T\n", n)
}
@ -39,14 +47,12 @@ func (p *printer) printObjectItem(o *ast.ObjectItem) []byte {
for i, k := range o.Keys {
buf.WriteString(k.Token.Text)
if i != len(o.Keys)-1 || len(o.Keys) == 1 {
buf.WriteString(" ")
}
buf.WriteByte(blank)
// reach end of key
if i == len(o.Keys)-1 {
buf.WriteString("=")
buf.WriteString(" ")
buf.WriteByte(blank)
}
}
@ -58,6 +64,26 @@ func (p *printer) printLiteral(l *ast.LiteralType) []byte {
return []byte(l.Token.Text)
}
func (p *printer) printObjectType(o *ast.ObjectType) []byte {
var buf bytes.Buffer
buf.WriteString("{")
buf.WriteByte(newline)
for _, item := range o.List.Items {
// buf.WriteByte(tab)
// buf.Write(p.printObjectItem(item))
a := p.printObjectItem(item)
a = indent(a)
buf.Write(a)
buf.WriteByte(newline)
}
buf.WriteString("}")
return buf.Bytes()
}
func (p *printer) printList(l *ast.ListType) []byte {
var buf bytes.Buffer
buf.WriteString("[")
@ -65,20 +91,44 @@ func (p *printer) printList(l *ast.ListType) []byte {
for i, item := range l.List {
if item.Pos().Line != l.Lbrack.Line {
// not same line
buf.WriteString("\n")
buf.WriteByte(newline)
}
buf.WriteByte(tab)
buf.Write(p.printNode(item))
if i != len(l.List)-1 {
buf.WriteString(",")
buf.WriteString(" ")
buf.WriteByte(blank)
} else if item.Pos().Line != l.Lbrack.Line {
buf.WriteString(",")
buf.WriteString("\n")
buf.WriteByte(newline)
}
}
buf.WriteString("]")
return buf.Bytes()
}
func writeBlank(buf io.ByteWriter, indent int) {
for i := 0; i < indent; i++ {
buf.WriteByte(blank)
}
}
func indent(buf []byte) []byte {
splitted := bytes.Split(buf, []byte{newline})
newBuf := make([]byte, len(splitted))
for i, s := range splitted {
s = append(s, 0)
copy(s[1:], s[0:])
s[0] = tab
newBuf = append(newBuf, s...)
if i != len(splitted)-1 {
newBuf = append(newBuf, newline)
}
}
return newBuf
}

View File

@ -1,7 +1,6 @@
package printer
import (
"fmt"
"io"
"text/tabwriter"
@ -15,24 +14,12 @@ type printer struct {
}
func (p *printer) output() []byte {
fmt.Println("STARTING OUTPUT")
return p.printNode(p.node)
}
// A Mode value is a set of flags (or 0). They control printing.
type Mode uint
const (
RawFormat Mode = 1 << iota // do not use a tabwriter; if set, UseSpaces is ignored
TabIndent // use tabs for indentation independent of UseSpaces
UseSpaces // use spaces instead of tabs for alignment
)
// A Config node controls the output of Fprint.
type Config struct {
Mode Mode // default: 0
Tabwidth int // default: 4
Indent int // default: 0 (all code is indented at least by this much)
SpaceWidth int // if set, it will use spaces instead of tabs for alignment
}
func (c *Config) fprint(output io.Writer, node ast.Node) error {
@ -41,32 +28,6 @@ func (c *Config) fprint(output io.Writer, node ast.Node) error {
node: node,
}
// TODO(arslan): implement this
// redirect output through a trimmer to eliminate trailing whitespace
// (Input to a tabwriter must be untrimmed since trailing tabs provide
// formatting information. The tabwriter could provide trimming
// functionality but no tabwriter is used when RawFormat is set.)
// output = &trimmer{output: output}
// redirect output through a tabwriter if necessary
if c.Mode&RawFormat == 0 {
minwidth := c.Tabwidth
padchar := byte('\t')
if c.Mode&UseSpaces != 0 {
padchar = ' '
}
twmode := tabwriter.DiscardEmptyColumns
if c.Mode&TabIndent != 0 {
minwidth = 0
twmode |= tabwriter.TabIndent
}
output = tabwriter.NewWriter(output, minwidth, c.Tabwidth, 1, padchar, twmode)
}
// write printer result via tabwriter/trimmer to output
if _, err := output.Write(p.output()); err != nil {
return err
}
@ -87,5 +48,5 @@ func (c *Config) Fprint(output io.Writer, node ast.Node) error {
// Fprint "pretty-prints" an HCL node to output
// It calls Config.Fprint with default settings.
func Fprint(output io.Writer, node ast.Node) error {
return (&Config{Tabwidth: 4}).Fprint(output, node)
return (&Config{}).Fprint(output, node)
}

View File

@ -1,6 +1,7 @@
package printer
import (
"fmt"
"os"
"testing"
@ -58,8 +59,8 @@ output "web_ip" {
`
func TestPrint(t *testing.T) {
// node, err := parser.Parse([]byte(complexHcl))
node, err := parser.Parse([]byte(listHCL2))
node, err := parser.Parse([]byte(complexHcl))
// node, err := parser.Parse([]byte(listHCL2))
if err != nil {
t.Fatal(err)
}
@ -67,4 +68,6 @@ func TestPrint(t *testing.T) {
if err := Fprint(os.Stdout, node); err != nil {
t.Error(err)
}
fmt.Println("")
}