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:
@@ -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)
|
||||
|
Reference in New Issue
Block a user