Add comprehensive Zig syntax test coverage

- Add support for error sets and error union types
- Implement for loops with index and payload syntax
- Add defer, break, continue, and switch statements
- Support unary expressions and array indexing
- Add unreachable expression and test declarations
- Extend AST with new type expressions (array, error union)
- Update formatter to handle all new syntax elements
- Fix formatting for switch prongs, payloads, and blocks
This commit is contained in:
2025-06-05 20:44:49 -05:00
parent 50b38254ab
commit 258b3c8e9b
4 changed files with 529 additions and 7 deletions

View File

@@ -98,6 +98,7 @@ func writeDecl(f *formatter, decl Decl) {
f.writef(") ")
writeTypeExpr(f, d.ReturnType)
writeBlock(f, d.Body)
f.writef("\n")
case *GlobalVarDecl:
if d.Flags&GlobalVarConst != 0 {
f.writef("const %s = ", d.Name)
@@ -109,6 +110,15 @@ func writeDecl(f *formatter, decl Decl) {
case *ContainerDecl:
f.writef("struct ")
writeStructBody(f, d)
case *ErrorSetDecl:
writeExpr(f, d)
case *TestDecl:
f.writef("test ")
if d.Name != "" {
f.writef(`"%s"`, d.Name)
}
writeBlock(f, d.Block)
f.writef("\n")
}
}
@@ -133,6 +143,15 @@ func writeTypeExpr(f *formatter, typ TypeExpr) {
case *PrefixTypeExpr:
f.writef("%s", t.Op)
writeTypeExpr(f, t.Base)
case *ArrayTypeExpr:
f.writef("[")
writeExpr(f, t.Size)
f.writef("]")
writeTypeExpr(f, t.Elem)
case *ErrorUnionTypeExpr:
writeTypeExpr(f, t.ErrSet)
f.writef("!")
writeTypeExpr(f, t.Type)
case nil:
// nothing
default:
@@ -198,6 +217,12 @@ func writeStmt(f *formatter, stmt Stmt) {
f.writef("if (")
writeExpr(f, s.Cond)
f.writef(")")
// Handle payload if present
if s.Payload != nil {
f.writef(" ")
writePayload(f, s.Payload)
}
// Always write the then branch as a block
if block, ok := s.Then.(*BlockStmt); ok {
@@ -233,7 +258,156 @@ func writeStmt(f *formatter, stmt Stmt) {
f.writef(" ")
writeStmt(f, s.Body)
}
} else if s.Kind == "for" {
f.writef("for (")
if fp, ok := s.Prefix.(*ForPrefix); ok {
for i, arg := range fp.Args {
if i > 0 {
f.writef(", ")
}
writeExpr(f, arg.Expr)
if arg.From != nil {
if lit, ok := arg.From.(*Literal); ok && lit.Value == "" {
f.writef("..")
} else {
f.writef("...")
writeExpr(f, arg.From)
}
}
}
f.writef(")")
if fp.Payload != nil {
f.writef(" ")
writePayload(f, fp.Payload)
}
}
// Always write the body as a block
if block, ok := s.Body.(*BlockStmt); ok {
writeBlock(f, block.Block)
} else {
f.writef(" ")
writeStmt(f, s.Body)
}
}
case *DeferStmt:
if s.ErrDefer {
f.writef("errdefer")
} else {
f.writef("defer")
}
f.writef(" ")
writeStmt(f, s.Stmt)
case *BreakStmt:
f.writef("break")
if s.Label != "" {
f.writef(" :%s", s.Label)
}
if s.Value != nil {
f.writef(" ")
writeExpr(f, s.Value)
}
f.writef(";")
case *ContinueStmt:
f.writef("continue")
if s.Label != "" {
f.writef(" :%s", s.Label)
}
f.writef(";")
case *SwitchStmt:
f.writef("switch (")
writeExpr(f, s.Cond)
f.writef(") {\n")
f.indent++
for _, prong := range s.Prongs {
f.writeIndent()
writeSwitchProng(f, prong)
f.writef("\n")
}
f.indent--
f.writeIndent()
f.writef("}")
}
}
// writePayload emits a payload (|x|, |*x|, |*x, y|, etc).
func writePayload(f *formatter, payload *Payload) {
f.writef("|")
for i, name := range payload.Names {
if i > 0 {
f.writef(", ")
}
if payload.Pointers[i] {
f.writef("*")
}
f.writef("%s", name)
}
f.writef("|")
}
// writeSwitchProng emits a switch prong.
func writeSwitchProng(f *formatter, prong *SwitchProng) {
for i, c := range prong.Cases {
if i > 0 {
f.writef(", ")
}
if c.IsElse {
f.writef("else")
} else {
writeExpr(f, c.Expr)
if c.To != nil {
f.writef("...")
writeExpr(f, c.To)
}
}
}
f.writef(" => ")
// Check if the expression is actually a statement (like return or break)
if stmt, ok := prong.Expr.(Stmt); ok {
// If it's a block, write it directly without the leading space
if blockStmt, isBlock := stmt.(*BlockStmt); isBlock {
f.writef("{\n")
f.indent++
for _, s := range blockStmt.Block.Stmts {
f.writeIndent()
writeStmt(f, s)
f.writef("\n")
}
f.indent--
f.writeIndent()
f.writef("},")
} else {
// For single statements, write without the semicolon
switch s := stmt.(type) {
case *ReturnStmt:
f.writef("return")
if s.Value != nil {
f.writef(" ")
writeExpr(f, s.Value)
}
case *BreakStmt:
f.writef("break")
if s.Label != "" {
f.writef(" :%s", s.Label)
}
if s.Value != nil {
f.writef(" ")
writeExpr(f, s.Value)
}
case *ContinueStmt:
f.writef("continue")
if s.Label != "" {
f.writef(" :%s", s.Label)
}
case *ExprStmt:
writeExpr(f, s.Expr)
default:
writeStmt(f, stmt)
}
f.writef(",")
}
} else {
writeExpr(f, prong.Expr)
f.writef(",")
}
}
@@ -266,14 +440,20 @@ func writeExpr(f *formatter, expr Expr) {
if e.Empty {
f.writef(".{}")
} else if len(e.Values) > 0 {
f.writef(".{")
for i, v := range e.Values {
if i > 0 {
f.writef(", ")
if len(e.Values) == 1 {
f.writef(".{")
writeExpr(f, e.Values[0])
f.writef("}")
} else {
f.writef(".{ ")
for i, v := range e.Values {
if i > 0 {
f.writef(", ")
}
writeExpr(f, v)
}
writeExpr(f, v)
f.writef(" }")
}
f.writef("}")
}
case *ContainerDecl:
if e.Kind == "struct" {
@@ -289,6 +469,26 @@ func writeExpr(f *formatter, expr Expr) {
writeExpr(f, e.Left)
f.writef(" %s ", e.Op)
writeExpr(f, e.Right)
case *UnaryExpr:
f.writef("%s", e.Op)
writeExpr(f, e.Expr)
case *IndexExpr:
writeExpr(f, e.Receiver)
f.writef("[")
writeExpr(f, e.Index)
f.writef("]")
case *UnreachableExpr:
f.writef("unreachable")
case *ErrorSetDecl:
f.writef("error{\n")
f.indent++
for _, name := range e.Names {
f.writeIndent()
f.writef("%s,\n", name)
}
f.indent--
f.writeIndent()
f.writef("}")
}
}
@@ -321,7 +521,6 @@ func writeStructBody(f *formatter, decl *ContainerDecl) {
f.writef("\n")
f.writeIndent()
writeDecl(f, member.Decl)
f.writef("\n")
}
}
f.indent--