Add comprehensive test suite for Zig AST

- Add TestAllExpressionTypes covering all expression nodes
- Add TestAllStatementTypes covering all statement nodes
- Add TestAllTypeExpressions covering all type expressions
- Add TestControlFlowPatterns demonstrating complex control flow
- Fix formatter for field initializers, type spacing, and labels
- Fix handling of orelse blocks, switch payloads, and errdefer
- Remove duplicate test functions from previous edits
- Tests serve as documentation/examples for AST construction
This commit is contained in:
2025-06-05 22:00:41 -05:00
parent 2af696078d
commit d5f346cf8b
2 changed files with 739 additions and 7 deletions

View File

@@ -150,6 +150,10 @@ func writeTypeExpr(f *formatter, typ TypeExpr) {
f.writef("%s", t.Name)
case *PrefixTypeExpr:
f.writef("%s", t.Op)
// Add space after multi-character operators like *const
if len(t.Op) > 1 && !strings.HasSuffix(t.Op, "]") {
f.writef(" ")
}
writeTypeExpr(f, t.Base)
case *ArrayTypeExpr:
f.writef("[")
@@ -223,7 +227,25 @@ func writeStmt(f *formatter, stmt Stmt) {
}
f.writef(";")
case *BlockStmt:
writeBlock(f, s.Block)
// Check if this is a labeled block
if s.Block != nil && s.Block.Label != "" {
f.writef("%s:", s.Block.Label)
}
// For standalone blocks, we need to write the block without a leading space
if s.Block == nil {
f.writef(";")
} else {
f.writef("{\n")
f.indent++
for _, stmt := range s.Block.Stmts {
f.writeIndent()
writeStmt(f, stmt)
f.writef("\n")
}
f.indent--
f.writeIndent()
f.writef("}")
}
case *IfStmt:
f.writef("if (")
writeExpr(f, s.Cond)
@@ -252,6 +274,11 @@ func writeStmt(f *formatter, stmt Stmt) {
}
}
case *LoopStmt:
// Handle label if the body is a labeled block
if block, ok := s.Body.(*BlockStmt); ok && block.Block != nil && block.Block.Label != "" {
f.writef("%s: ", block.Block.Label)
}
if s.Kind == LoopWhile {
f.writef("while (")
if wp, ok := s.Prefix.(*WhilePrefix); ok {
@@ -264,7 +291,16 @@ func writeStmt(f *formatter, stmt Stmt) {
f.writef(")")
// Always write the body as a block
if block, ok := s.Body.(*BlockStmt); ok {
// Don't write the label again, just the block content
origLabel := ""
if block.Block != nil {
origLabel = block.Block.Label
block.Block.Label = "" // Temporarily clear to avoid double printing
}
writeBlock(f, block.Block)
if block.Block != nil {
block.Block.Label = origLabel // Restore
}
} else {
f.writef(" ")
writeStmt(f, s.Body)
@@ -294,7 +330,16 @@ func writeStmt(f *formatter, stmt Stmt) {
}
// Always write the body as a block
if block, ok := s.Body.(*BlockStmt); ok {
// Don't write the label again, just the block content
origLabel := ""
if block.Block != nil {
origLabel = block.Block.Label
block.Block.Label = "" // Temporarily clear to avoid double printing
}
writeBlock(f, block.Block)
if block.Block != nil {
block.Block.Label = origLabel // Restore
}
} else {
f.writef(" ")
writeStmt(f, s.Body)
@@ -303,6 +348,10 @@ func writeStmt(f *formatter, stmt Stmt) {
case *DeferStmt:
if s.ErrDefer {
f.writef("errdefer")
if s.Payload != nil {
f.writef(" ")
writePayload(f, s.Payload)
}
} else {
f.writef("defer")
}
@@ -372,6 +421,11 @@ func writeSwitchProng(f *formatter, prong *SwitchProng) {
}
}
f.writef(" => ")
// Write payload if present after =>
if prong.Payload != nil {
writePayload(f, prong.Payload)
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
@@ -438,8 +492,13 @@ func writeExpr(f *formatter, expr Expr) {
}
f.writef(")")
case *FieldAccessExpr:
writeExpr(f, e.Receiver)
f.writef(".%s", e.Field)
// Handle enum field access like .value (no receiver)
if id, ok := e.Receiver.(*Identifier); ok && id.Name == "" {
f.writef(".%s", e.Field)
} else {
writeExpr(f, e.Receiver)
f.writef(".%s", e.Field)
}
case *Literal:
switch e.Kind {
case LiteralString:
@@ -465,6 +524,16 @@ func writeExpr(f *formatter, expr Expr) {
}
f.writef(" }")
}
} else if len(e.Fields) > 0 {
f.writef(".{ ")
for i, field := range e.Fields {
if i > 0 {
f.writef(", ")
}
f.writef(".%s = ", field.Name)
writeExpr(f, field.Value)
}
f.writef(" }")
}
case *ContainerDecl:
switch e.Kind {
@@ -475,7 +544,14 @@ func writeExpr(f *formatter, expr Expr) {
f.writef("enum ")
writeStructBody(f, e)
case ContainerUnion:
f.writef("union ")
f.writef("union")
if e.TagType != nil {
f.writef("(")
writeTypeExpr(f, e.TagType)
f.writef(") ")
} else {
f.writef(" ")
}
writeStructBody(f, e)
case ContainerOpaque:
f.writef("opaque ")
@@ -489,7 +565,17 @@ func writeExpr(f *formatter, expr Expr) {
case *BinaryExpr:
writeExpr(f, e.Left)
f.writef(" %s ", e.Op)
writeExpr(f, e.Right)
// Special handling for orelse with block
if e.Op == "orelse" {
if _, ok := e.Right.(*BlockStmt); ok {
// For orelse blocks, we need special formatting
writeStmt(f, e.Right.(Stmt))
} else {
writeExpr(f, e.Right)
}
} else {
writeExpr(f, e.Right)
}
case *UnaryExpr:
f.writef("%s", e.Op)
writeExpr(f, e.Expr)
@@ -562,6 +648,9 @@ func writeExpr(f *formatter, expr Expr) {
f.indent--
f.writeIndent()
f.writef("}")
case *LabeledBlock:
f.writef("%s:", e.Label)
writeBlock(f, e.Block)
}
}
@@ -581,8 +670,11 @@ func writeStructBody(f *formatter, decl *ContainerDecl) {
} else {
// regular field
f.writeIndent()
f.writef("%s: ", member.Field.Name)
writeTypeExpr(f, member.Field.Type)
f.writef("%s", member.Field.Name)
if member.Field.Type != nil {
f.writef(": ")
writeTypeExpr(f, member.Field.Type)
}
if member.Field.Value != nil {
f.writef(" = ")
writeExpr(f, member.Field.Value)