196 lines
4.2 KiB
Go
196 lines
4.2 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
|
|
}
|
|
|
|
// indentStr defines the string used for each indentation level (4 spaces).
|
|
const indentStr = " "
|
|
|
|
// Writef writes formatted text to the underlying writer and updates line/col counters.
|
|
// It also handles indentation after newlines when appropriate.
|
|
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
|
|
// After a newline, write indentation for the next line unless it's a closing brace or another newline.
|
|
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)
|
|
}
|
|
}
|
|
|
|
// writeIndent writes the current indentation level to the output.
|
|
// Call this at the start of a new line before writing statements or closing braces.
|
|
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)
|
|
}
|
|
}
|
|
|
|
// Write is the entry point for formatting a Zig AST.
|
|
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
|
|
}
|
|
|
|
// writeDecl emits a top-level declaration.
|
|
func writeDecl(f *formatter, decl Decl) {
|
|
switch d := decl.(type) {
|
|
case *FnDecl:
|
|
f.Writef("\nfn %s(", d.Name)
|
|
writeParams(f, d.Params)
|
|
f.Writef(") ")
|
|
writeTypeExpr(f, d.ReturnType)
|
|
writeBlock(f, d.Body)
|
|
case *GlobalVarDecl:
|
|
if d.Const {
|
|
f.Writef("const %s = ", d.Name)
|
|
} else {
|
|
f.Writef("var %s = ", d.Name)
|
|
}
|
|
writeExpr(f, d.Value)
|
|
f.Writef(";\n")
|
|
}
|
|
}
|
|
|
|
// writeParams emits function parameters, separated by commas.
|
|
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)
|
|
}
|
|
}
|
|
|
|
// writeTypeExpr emits a type expression.
|
|
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)
|
|
}
|
|
}
|
|
|
|
// writeBlock emits a block, handling indentation for statements and the closing brace.
|
|
func writeBlock(f *formatter, block *Block) {
|
|
if block == nil {
|
|
f.Writef(";")
|
|
return
|
|
}
|
|
f.Writef(" {\n")
|
|
f.indent++ // Increase indentation for block contents.
|
|
for i, stmt := range block.Stmts {
|
|
f.writeIndent() // Indent each statement.
|
|
writeStmt(f, stmt)
|
|
if i < len(block.Stmts)-1 {
|
|
f.Writef("\n")
|
|
}
|
|
}
|
|
f.indent-- // Decrease indentation before closing brace.
|
|
f.Writef("\n")
|
|
f.writeIndent() // Indent the closing brace.
|
|
f.Writef("}\n")
|
|
}
|
|
|
|
// writeStmt emits a statement. Indentation is handled by the caller (writeBlock).
|
|
func writeStmt(f *formatter, stmt Stmt) {
|
|
switch s := stmt.(type) {
|
|
case *ReturnStmt:
|
|
f.Writef("return")
|
|
if s.Value != nil {
|
|
f.Writef(" ")
|
|
writeExpr(f, s.Value)
|
|
}
|
|
f.Writef(";")
|
|
case *ExprStmt:
|
|
writeExpr(f, s.Expr)
|
|
f.Writef(";")
|
|
}
|
|
}
|
|
|
|
// writeExpr emits an expression.
|
|
func writeExpr(f *formatter, expr Expr) {
|
|
switch e := expr.(type) {
|
|
case *Identifier:
|
|
f.Writef("%s", e.Name)
|
|
case *CallExpr:
|
|
writeExpr(f, e.Fun)
|
|
f.Writef("(")
|
|
for i, arg := range e.Args {
|
|
if i > 0 {
|
|
f.Writef(", ")
|
|
}
|
|
writeExpr(f, arg)
|
|
}
|
|
f.Writef(")")
|
|
case *FieldAccessExpr:
|
|
writeExpr(f, e.Receiver)
|
|
f.Writef(".%s", e.Field)
|
|
case *Literal:
|
|
switch e.Kind {
|
|
case "string":
|
|
f.Writef("%q", e.Value)
|
|
default:
|
|
f.Writef("%v", e.Value)
|
|
}
|
|
case *InitListExpr:
|
|
if e.Empty {
|
|
f.Writef(".{}")
|
|
} else {
|
|
f.Writef(".{")
|
|
// TODO
|
|
f.Writef("}")
|
|
}
|
|
}
|
|
}
|