133 lines
2.4 KiB
Go

package zig
import (
"fmt"
"io"
)
type formatter struct {
w io.Writer
line int // 1-based
col int // 1-based, reset to 1 after newline
indent int // indentation level
}
const indentStr = " " // 4 spaces per indent
func (f *formatter) Writef(format string, a ...any) {
s := fmt.Sprintf(format, a...)
for i, r := range s {
if r == '\n' {
f.line++
f.col = 1
// If next character is not a newline and not end, and not a closing brace, write indentation
if i+1 < len(s) && s[i+1] != '\n' && s[i+1] != '}' {
f.writeIndent()
}
} else {
if f.col == 0 {
f.col = 1
} else {
f.col++
}
}
}
if _, err := f.w.Write([]byte(s)); err != nil {
panic(err)
}
}
func (f *formatter) writeIndent() {
for i := 0; i < f.indent; i++ {
if _, err := f.w.Write([]byte(indentStr)); err != nil {
panic(err)
}
f.col += len(indentStr)
}
}
func Write(w io.Writer, root *Root) (err error) {
defer func() {
if r := recover(); r != nil {
if e, ok := r.(error); ok {
err = e
} else {
panic(r)
}
}
}()
f := &formatter{w: w, line: 1, col: 1, indent: 0}
if root.ContainerDocComment != "" {
f.Writef("//! %s\n\n", root.ContainerDocComment)
}
for _, member := range root.ContainerMembers {
// Only handle Decl for now (fields not needed for hello world)
if member.Decl != nil {
writeDecl(f, member.Decl)
}
}
return nil
}
func writeDecl(f *formatter, decl Decl) {
switch fn := decl.(type) {
case *FnDecl:
f.Writef("fn %s(", fn.Name)
writeParams(f, fn.Params)
f.Writef(") ")
writeTypeExpr(f, fn.ReturnType)
writeBlock(f, fn.Body)
}
}
func writeParams(f *formatter, params []*ParamDecl) {
for i, param := range params {
if i > 0 {
f.Writef(", ")
}
if param.Name != "" {
f.Writef("%s: ", param.Name)
}
writeTypeExpr(f, param.Type)
}
}
func writeTypeExpr(f *formatter, typ TypeExpr) {
switch t := typ.(type) {
case *Identifier:
f.Writef("%s", t.Name)
case nil:
// nothing
default:
f.Writef("%v", t)
}
}
func writeBlock(f *formatter, block *Block) {
if block == nil {
f.Writef(";")
return
}
f.Writef(" {\n")
f.indent++
for i, stmt := range block.Stmts {
f.writeIndent()
writeStmt(f, stmt)
if i < len(block.Stmts)-1 {
f.Writef("\n")
}
}
f.indent--
f.Writef("\n")
f.writeIndent()
f.Writef("}\n")
}
func writeStmt(f *formatter, stmt Stmt) {
switch stmt.(type) {
case *ReturnStmt:
f.Writef("return;")
}
}