Compare commits

...

5 Commits

Author SHA1 Message Date
bea04d6810 Add comprehensive test system for Go-to-Zig compiler
- Implement end-to-end test runner for compilation and behavior tests
- Add test cases for basic print functionality
- Refactor translator to use proper AST generation
- Remove redundant programs directory in favor of tests
2025-06-05 22:36:38 -05:00
d5f346cf8b 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
2025-06-05 22:00:41 -05:00
2af696078d Improve Zig AST developer experience
- Convert string-based type discrimination to type-safe enums
  - ContainerKind (struct, enum, union, opaque)
  - LiteralKind (int, float, string, char)
  - LoopKind (for, while)
- Remove duplicate AST nodes (consolidated init lists, removed unused types)
- Add comprehensive helper functions for all AST constructions
- Implement formatters for all AST nodes (expressions, statements, types)
- Add typed literal constructors: IntLit, FloatLit, StringLit, CharLit
- Improve documentation and add deprecation notices

This makes the AST more intuitive and type-safe for developers.
2025-06-05 21:10:50 -05:00
258b3c8e9b 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
2025-06-05 20:44:49 -05:00
50b38254ab Added 2 tests and asthelpers.go 2025-05-29 16:27:36 -05:00
14 changed files with 2450 additions and 197 deletions

View File

@@ -1,5 +1,15 @@
run: run:
go run internal/main.go -o hello.zig programs/hello.go && zig run hello.zig go run internal/main.go -o hello.zig programs/hello.go && zig run hello.zig
test: test: test-unit test-integration
test-unit:
go test ./internal/zig go test ./internal/zig
test-integration:
go run ./cmd/testrunner
test-quick:
go run ./cmd/testrunner
.PHONY: run test test-unit test-integration test-quick

228
cmd/testrunner/main.go Normal file
View File

@@ -0,0 +1,228 @@
package main
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"time"
)
type TestResult struct {
Name string
Passed bool
Output string
Error string
Elapsed time.Duration
}
type TestCase struct {
GoFile string
ExpectedFile string
ErrorFile string
StdinFile string
ArgsFile string
}
func main() {
// Find all test cases
testCases, err := findTestCases("tests")
if err != nil {
fmt.Fprintf(os.Stderr, "Error finding tests: %v\n", err)
os.Exit(1)
}
if len(testCases) == 0 {
fmt.Println("No tests found")
os.Exit(0)
}
fmt.Printf("Running %d tests...\n\n", len(testCases))
// Run tests in parallel
results := runTests(testCases)
// Print results
passed := 0
failed := 0
for _, result := range results {
if result.Passed {
fmt.Printf("✓ %s (%v)\n", result.Name, result.Elapsed)
passed++
} else {
fmt.Printf("✗ %s (%v)\n", result.Name, result.Elapsed)
fmt.Printf(" Error: %s\n", result.Error)
if result.Output != "" {
fmt.Printf(" Output:\n%s\n", indent(result.Output, " "))
}
failed++
}
}
fmt.Printf("\n%d passed, %d failed\n", passed, failed)
if failed > 0 {
os.Exit(1)
}
}
func findTestCases(dir string) ([]TestCase, error) {
var testCases []TestCase
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if strings.HasSuffix(path, ".go") {
base := strings.TrimSuffix(path, ".go")
testCase := TestCase{
GoFile: path,
ExpectedFile: base + ".expected",
ErrorFile: base + ".error",
StdinFile: base + ".stdin",
ArgsFile: base + ".args",
}
// Check if either .expected or .error exists
if fileExists(testCase.ExpectedFile) || fileExists(testCase.ErrorFile) {
testCases = append(testCases, testCase)
}
}
return nil
})
return testCases, err
}
func runTests(testCases []TestCase) []TestResult {
results := make([]TestResult, len(testCases))
var wg sync.WaitGroup
for i, tc := range testCases {
wg.Add(1)
go func(idx int, testCase TestCase) {
defer wg.Done()
results[idx] = runTest(testCase)
}(i, tc)
}
wg.Wait()
return results
}
func runTest(tc TestCase) TestResult {
start := time.Now()
testName := filepath.Base(strings.TrimSuffix(tc.GoFile, ".go"))
result := TestResult{
Name: testName,
Elapsed: time.Since(start),
}
// Create temporary directory for test outputs
tempDir, err := os.MkdirTemp("", "go-zig-test-*")
if err != nil {
result.Error = fmt.Sprintf("Failed to create temp dir: %v", err)
return result
}
defer os.RemoveAll(tempDir)
zigFile := filepath.Join(tempDir, testName+".zig")
// Compile Go to Zig
compileCmd := exec.Command("go", "run", "./internal/main.go", "-o", zigFile, tc.GoFile)
var compileOut bytes.Buffer
compileCmd.Stderr = &compileOut
compileCmd.Stdout = &compileOut
if err := compileCmd.Run(); err != nil {
// Check if this is an expected compilation error
if fileExists(tc.ErrorFile) {
expectedError, _ := os.ReadFile(tc.ErrorFile)
actualError := strings.TrimSpace(compileOut.String())
expectedErrorStr := strings.TrimSpace(string(expectedError))
if actualError == expectedErrorStr {
result.Passed = true
} else {
result.Error = fmt.Sprintf("Expected error:\n%s\n\nActual error:\n%s", expectedErrorStr, actualError)
}
} else {
result.Error = fmt.Sprintf("Compilation failed: %v\nOutput: %s", err, compileOut.String())
}
result.Elapsed = time.Since(start)
return result
}
// If we expected a compilation error but didn't get one
if fileExists(tc.ErrorFile) {
result.Error = "Expected compilation to fail, but it succeeded"
result.Elapsed = time.Since(start)
return result
}
// Run the Zig program
runCmd := exec.Command("zig", "run", zigFile)
// Set up stdin if provided
if fileExists(tc.StdinFile) {
stdinData, _ := os.ReadFile(tc.StdinFile)
runCmd.Stdin = bytes.NewReader(stdinData)
}
// Set up args if provided
if fileExists(tc.ArgsFile) {
argsData, _ := os.ReadFile(tc.ArgsFile)
args := strings.Fields(string(argsData))
runCmd.Args = append(runCmd.Args, args...)
}
// Capture both stdout and stderr (Zig's debug.print goes to stderr)
output, err := runCmd.CombinedOutput()
if err != nil {
result.Error = fmt.Sprintf("Execution failed: %v\nOutput: %s", err, string(output))
result.Output = string(output)
result.Elapsed = time.Since(start)
return result
}
// Compare output with expected
if fileExists(tc.ExpectedFile) {
expectedOutput, _ := os.ReadFile(tc.ExpectedFile)
actualOutput := string(output)
if actualOutput == string(expectedOutput) {
result.Passed = true
} else {
result.Error = fmt.Sprintf("Output mismatch.\nExpected:\n%s\nActual:\n%s",
string(expectedOutput), actualOutput)
result.Output = actualOutput
}
} else {
// No expected file, just check it runs without error
result.Passed = true
}
result.Elapsed = time.Since(start)
return result
}
func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
func indent(s string, prefix string) string {
lines := strings.Split(s, "\n")
for i, line := range lines {
if line != "" {
lines[i] = prefix + line
}
}
return strings.Join(lines, "\n")
}

5
hello.zig Normal file
View File

@@ -0,0 +1,5 @@
const std = @import("std");
pub fn main() void {
std.debug.print("Hello, world\n", .{});
}

View File

@@ -8,6 +8,8 @@ import (
"go/token" "go/token"
"os" "os"
"strings" "strings"
"git.frop.prof/luke/go-zig-compiler/internal/zig"
) )
var ( var (
@@ -36,57 +38,122 @@ func main() {
panic(err) panic(err)
} }
output, err := generate(f) zigRoot, err := translateToZig(f)
if err != nil { if err != nil {
panic(err) panic(err)
} }
outputFile, err := os.Create(*outputFilepath) outputFile, err := os.Create(*outputFilepath)
if err != nil { if err != nil {
panic(err) panic(err)
} }
_, err = outputFile.WriteString(output) defer outputFile.Close()
err = zig.Write(outputFile, zigRoot)
if err != nil { if err != nil {
panic(err) panic(err)
} }
fmt.Printf("%v:\n", *outputFilepath)
fmt.Println("--------------------")
fmt.Println(output)
} }
func generate(f *ast.File) (string, error) { func translateToZig(f *ast.File) (*zig.Root, error) {
sb := new(strings.Builder) // Create the root AST node
root := &zig.Root{
def := f.Decls[0].(*ast.FuncDecl) ContainerMembers: []*zig.ContainerMember{},
if def.Name.Name != "main" {
return "", fmt.Errorf("must have main")
} }
sb.WriteString(`const std = @import("std");`) // Add the std import
sb.WriteString("\npub fn main() void {\n") root.ContainerMembers = append(root.ContainerMembers, &zig.ContainerMember{
Decl: zig.DeclareGlobalVar("std",
zig.Call(zig.Id("@import"), zig.StringLit("std")),
zig.GlobalVarConst,
),
})
stmt := def.Body.List[0].(*ast.ExprStmt) // Find and translate the main function
call := stmt.X.(*ast.CallExpr) for _, decl := range f.Decls {
fn := call.Fun.(*ast.Ident) if fn, ok := decl.(*ast.FuncDecl); ok && fn.Name.Name == "main" {
mainFunc, err := translateMainFunction(fn)
if fn.Name == "print" { if err != nil {
sb.WriteString(fmt.Sprintf(`std.debug.print(`)) return nil, err
args := call.Args
for _, arg := range args {
if s, ok := arg.(*ast.BasicLit); ok {
sb.WriteString(fmt.Sprintf("%s", s.Value))
} else {
panic("WTF")
} }
root.ContainerMembers = append(root.ContainerMembers, &zig.ContainerMember{
Decl: mainFunc,
})
} }
sb.WriteString(", .{});\n")
} else {
return "", fmt.Errorf("expected printf")
} }
sb.WriteString("}\n") return root, nil
}
return sb.String(), nil
func translateMainFunction(fn *ast.FuncDecl) (*zig.FnDecl, error) {
// Create the main function
stmts := []zig.Stmt{}
// Translate each statement in the function body
for _, stmt := range fn.Body.List {
zigStmt, err := translateStatement(stmt)
if err != nil {
return nil, err
}
if zigStmt != nil {
stmts = append(stmts, zigStmt)
}
}
return zig.DeclareFn(
"main",
zig.Id("void"),
zig.NewBlock(stmts...),
nil,
zig.FnExport,
), nil
}
func translateStatement(stmt ast.Stmt) (zig.Stmt, error) {
switch s := stmt.(type) {
case *ast.ExprStmt:
// Handle expression statements (like function calls)
expr, err := translateExpression(s.X)
if err != nil {
return nil, err
}
return zig.NewExprStmt(expr), nil
default:
return nil, fmt.Errorf("unsupported statement type: %T", stmt)
}
}
func translateExpression(expr ast.Expr) (zig.Expr, error) {
switch e := expr.(type) {
case *ast.CallExpr:
// Handle function calls
if ident, ok := e.Fun.(*ast.Ident); ok && ident.Name == "print" {
// Translate print() to std.debug.print()
args := []zig.Expr{}
// First argument is the format string
if len(e.Args) > 0 {
if lit, ok := e.Args[0].(*ast.BasicLit); ok && lit.Kind == token.STRING {
// Remove quotes and use the string value
args = append(args, zig.StringLit(strings.Trim(lit.Value, `"`)))
} else {
return nil, fmt.Errorf("print() requires a string literal argument")
}
}
// Second argument is always .{} for now
args = append(args, zig.InitList())
return zig.Call(
zig.FieldAccess(
zig.FieldAccess(zig.Id("std"), "debug"),
"print",
),
args...,
), nil
}
return nil, fmt.Errorf("unsupported function call: %v", e.Fun)
default:
return nil, fmt.Errorf("unsupported expression type: %T", expr)
}
} }

View File

@@ -2,9 +2,10 @@
package zig package zig
// Root is the top-level node of a Zig source file. // Root is the top-level node of a Zig source file.
// It represents the entire compilation unit.
type Root struct { type Root struct {
ContainerDocComment DocComment // //! Doc Comment (optional) ContainerDocComment DocComment // Module-level doc comment using //!
ContainerMembers []*ContainerMember ContainerMembers []*ContainerMember // Top-level declarations and fields
} }
type ContainerMember struct { type ContainerMember struct {
@@ -27,14 +28,21 @@ type Decl interface {
isDecl() isDecl()
} }
// FnFlags is a bitfield for function declaration options.
type FnFlags uint8
const (
FnExport FnFlags = 1 << iota
FnExtern
FnInline
FnNoInline
FnThreadLocal
)
// FnDecl represents a function declaration. // FnDecl represents a function declaration.
type FnDecl struct { type FnDecl struct {
Export bool Flags FnFlags
Extern bool
ExternName string // Optional string for extern ExternName string // Optional string for extern
Inline bool
NoInline bool
ThreadLocal bool
Name string // May be empty (anonymous) Name string // May be empty (anonymous)
Params []*ParamDecl Params []*ParamDecl
ByteAlign *Expr ByteAlign *Expr
@@ -47,13 +55,22 @@ type FnDecl struct {
func (*FnDecl) isDecl() {} func (*FnDecl) isDecl() {}
// GlobalVarDecl represents a global variable declaration. // GlobalVarFlags is a bitfield for global variable declaration options.
type GlobalVarFlags uint8
const (
GlobalVarConst GlobalVarFlags = 1 << iota
GlobalVarExport
GlobalVarExtern
GlobalVarThreadLocal
)
// GlobalVarDecl represents a top-level (global) variable or constant declaration.
// These are only allowed at the container/module scope and use a restricted syntax:
// no destructuring or multi-var declarations, just a single name and optional type/initializer.
type GlobalVarDecl struct { type GlobalVarDecl struct {
Export bool Flags GlobalVarFlags
Extern bool
ExternName string // Optional string for extern ExternName string // Optional string for extern
ThreadLocal bool
Const bool
Name string Name string
Type TypeExpr // Optional Type TypeExpr // Optional
ByteAlign *Expr ByteAlign *Expr
@@ -95,6 +112,35 @@ type ParamDecl struct {
Type TypeExpr // 'anytype' if empty Type TypeExpr // 'anytype' if empty
} }
// ContainerKind represents the kind of container (struct, enum, union, opaque).
type ContainerKind int
const (
ContainerStruct ContainerKind = iota
ContainerEnum
ContainerUnion
ContainerOpaque
)
// ContainerDecl represents a struct, enum, union, or opaque declaration.
type ContainerDecl struct {
Extern bool
Packed bool
Kind ContainerKind
TagType TypeExpr // Optional (for enum/union)
Fields []*ContainerMember
DocComment DocComment
}
func (*ContainerDecl) isDecl() {}
// ErrorSetDecl represents an error set declaration.
type ErrorSetDecl struct {
Names []string
}
func (*ErrorSetDecl) isDecl() {}
// Block represents a block of statements. // Block represents a block of statements.
type Block struct { type Block struct {
Label string // Optional Label string // Optional
@@ -113,7 +159,8 @@ type ExprStmt struct {
func (*ExprStmt) isStmt() {} func (*ExprStmt) isStmt() {}
// VarDeclStmt represents a variable or const declaration at statement level, supporting destructuring and multi-var declarations. // VarDeclStmt represents a local variable or constant declaration statement inside a function or block.
// These support destructuring and multi-var declarations, and are only valid at statement/block scope.
type VarDeclStmt struct { type VarDeclStmt struct {
Const bool Const bool
Pattern VarPattern // Destructuring or multiple variable names Pattern VarPattern // Destructuring or multiple variable names
@@ -190,10 +237,18 @@ type ContinueStmt struct {
func (*ContinueStmt) isStmt() {} func (*ContinueStmt) isStmt() {}
// LoopKind represents the kind of loop (for or while).
type LoopKind int
const (
LoopFor LoopKind = iota
LoopWhile
)
// LoopStmt represents a for/while loop statement. // LoopStmt represents a for/while loop statement.
type LoopStmt struct { type LoopStmt struct {
Inline bool // True if 'inline' is present Inline bool // True if 'inline' is present
Kind string // "for" or "while" Kind LoopKind // For or While
Prefix LoopPrefix // ForPrefix or WhilePrefix Prefix LoopPrefix // ForPrefix or WhilePrefix
Body Stmt // Loop body Body Stmt // Loop body
Else Stmt // Optional else branch Else Stmt // Optional else branch
@@ -276,21 +331,6 @@ type AsmInputItem struct {
Expr Expr Expr Expr
} }
// ContainerDecl represents a struct, enum, union, or opaque declaration.
type ContainerDecl struct {
Extern bool
Packed bool
Kind string // "struct", "enum", "union", "opaque"
TagType TypeExpr // Optional (for enum/union)
Fields []*ContainerMember
DocComment DocComment
}
// ErrorSetDecl represents an error set declaration.
type ErrorSetDecl struct {
Names []string
}
// InitListExpr represents an initializer list. // InitListExpr represents an initializer list.
// Exactly one of Fields, Values, or Empty must be set (non-nil/non-empty or true). // Exactly one of Fields, Values, or Empty must be set (non-nil/non-empty or true).
type InitListExpr struct { type InitListExpr struct {
@@ -310,9 +350,19 @@ type Identifier struct {
Name string // The identifier name Name string // The identifier name
} }
// LiteralKind represents the kind of literal.
type LiteralKind int
const (
LiteralInt LiteralKind = iota
LiteralFloat
LiteralString
LiteralChar
)
// Literal represents a literal value (int, float, string, char). // Literal represents a literal value (int, float, string, char).
type Literal struct { type Literal struct {
Kind string // "int", "float", "string", "char" Kind LiteralKind
Value string // The literal value as a string Value string // The literal value as a string
} }
@@ -370,17 +420,39 @@ type NosuspendExpr struct {
Expr Expr // The expression to evaluate with nosuspend Expr Expr // The expression to evaluate with nosuspend
} }
// ContinueExpr represents a 'continue' expression.
type ContinueExpr struct {
Label string // Optional label
}
// Expr is any expression. // Expr is any expression.
// This is an empty interface to allow maximum flexibility.
// Consider using type switches when working with expressions.
type Expr interface{} type Expr interface{}
// TypeExpr is any type expression. // TypeExpr is any type expression.
// This is an empty interface to allow maximum flexibility.
// Consider using type switches when working with type expressions.
type TypeExpr interface{} type TypeExpr interface{}
// PrefixTypeExpr represents a type with a string prefix. Examples include optionals and pointers.
type PrefixTypeExpr struct {
Op string
Base TypeExpr
}
// ArrayTypeExpr represents an array type ([N]T).
type ArrayTypeExpr struct {
Size Expr
Elem TypeExpr
}
// SliceTypeExpr represents a slice type ([]T).
type SliceTypeExpr struct {
Elem TypeExpr
}
// ErrorUnionTypeExpr represents an error union type (E!T).
type ErrorUnionTypeExpr struct {
ErrSet TypeExpr
Type TypeExpr
}
// DocComment represents a doc comment (/// or //! lines). // DocComment represents a doc comment (/// or //! lines).
// Newlines in the string automatically add more comments in the output. // Newlines in the string automatically add more comments in the output.
type DocComment string type DocComment string
@@ -456,43 +528,14 @@ type AwaitExpr struct {
// UnreachableExpr represents the 'unreachable' keyword. // UnreachableExpr represents the 'unreachable' keyword.
type UnreachableExpr struct{} type UnreachableExpr struct{}
// EmptyInitListExpr represents an empty initializer list '{}'. // Note: The following types were removed as they were duplicates:
type EmptyInitListExpr struct{} // - EmptyInitListExpr (use InitListExpr with Empty=true)
// - PositionalInitListExpr (use InitListExpr with Values)
// PositionalInitListExpr represents a positional initializer list '{expr, ...}'. // - FieldInitListExpr (use InitListExpr with Fields)
type PositionalInitListExpr struct { // - SwitchProngPayload (use Payload)
Values []Expr // Expressions in order // - SwitchProngCase (use SwitchCase)
} // - SwitchProngFull (use SwitchProng)
// - SwitchElseProng (use SwitchProng with SwitchCase.IsElse=true)
// FieldInitListExpr represents a field initializer list '{.field = expr, ...}'.
type FieldInitListExpr struct {
Fields []*FieldInit // Field initializers
}
// SwitchProngPayload represents a switch prong payload (|*x, y|).
type SwitchProngPayload struct {
Pointer bool
Names []string
}
// SwitchProngCase represents a single case in a switch prong.
type SwitchProngCase struct {
Expr Expr // The case expression
To Expr // Optional, for ranges
}
// SwitchProngFull represents a full switch prong with cases and payload.
type SwitchProngFull struct {
Inline bool
Cases []*SwitchProngCase // One or more cases
Payload *SwitchProngPayload // Optional
Expr Expr // The result expression
}
// SwitchElseProng represents an 'else' prong in a switch.
type SwitchElseProng struct {
Expr Expr // The result expression
}
// VarPattern represents a variable pattern for destructuring or multiple variable declarations. // VarPattern represents a variable pattern for destructuring or multiple variable declarations.
type VarPattern struct { type VarPattern struct {

370
internal/zig/asthelpers.go Normal file
View File

@@ -0,0 +1,370 @@
package zig
// Id creates an identifier expression.
func Id(name string) *Identifier {
return &Identifier{Name: name}
}
// IntLit creates an integer literal.
func IntLit(value string) *Literal {
return &Literal{Kind: LiteralInt, Value: value}
}
// FloatLit creates a float literal.
func FloatLit(value string) *Literal {
return &Literal{Kind: LiteralFloat, Value: value}
}
// StringLit creates a string literal.
func StringLit(value string) *Literal {
return &Literal{Kind: LiteralString, Value: value}
}
// CharLit creates a character literal.
func CharLit(value string) *Literal {
return &Literal{Kind: LiteralChar, Value: value}
}
// FieldAccess creates a field access expression.
func FieldAccess(recv Expr, field string) *FieldAccessExpr {
return &FieldAccessExpr{Receiver: recv, Field: field}
}
// Call creates a function call expression.
func Call(fun Expr, args ...Expr) *CallExpr {
return &CallExpr{Fun: fun, Args: args}
}
// InitList creates an initializer list expression.
func InitList(exprs ...Expr) *InitListExpr {
if len(exprs) == 0 {
return &InitListExpr{Empty: true}
}
return &InitListExpr{Values: exprs}
}
// DeclareFn creates a function declaration with flags.
func DeclareFn(name string, retType TypeExpr, body *Block, params []*ParamDecl, flags FnFlags) *FnDecl {
return &FnDecl{
Name: name,
ReturnType: retType,
Body: body,
Flags: flags,
Params: params,
}
}
// NewExprStmt creates an expression statement.
func NewExprStmt(expr Expr) *ExprStmt {
return &ExprStmt{Expr: expr}
}
// DeclareVarStmt creates a variable or const declaration statement.
func DeclareVarStmt(constant bool, names []string, typ TypeExpr, value Expr) *VarDeclStmt {
return &VarDeclStmt{
Const: constant,
Pattern: VarPattern{Names: names},
Type: typ,
Value: value,
}
}
// DeclareGlobalVar creates a global variable declaration with flags.
func DeclareGlobalVar(name string, value Expr, flags GlobalVarFlags) *GlobalVarDecl {
return &GlobalVarDecl{
Flags: flags,
Name: name,
Value: value,
}
}
// Return creates a return statement.
func Return(value Expr) *ReturnStmt {
return &ReturnStmt{Value: value}
}
// If creates an if statement.
func If(cond Expr, then, els Stmt) *IfStmt {
return &IfStmt{Cond: cond, Then: then, Else: els}
}
// IfWithPayload creates an if statement with a payload.
func IfWithPayload(cond Expr, payload *Payload, then, els Stmt) *IfStmt {
return &IfStmt{Cond: cond, Payload: payload, Then: then, Else: els}
}
// NewBlock creates a block of statements.
func NewBlock(stmts ...Stmt) *Block {
return &Block{Stmts: stmts}
}
// NewBlock creates a block statement containing a block of statements.
func NewBlockStmt(stmts ...Stmt) *BlockStmt {
return &BlockStmt{
Block: &Block{Stmts: stmts},
}
}
// Try creates a try expression.
func Try(expr Expr) *TryExpr {
return &TryExpr{Expr: expr}
}
// Binary creates a binary expression.
func Binary(op string, left, right Expr) *BinaryExpr {
return &BinaryExpr{Op: op, Left: left, Right: right}
}
// Param creates a function parameter declaration.
func Param(name string, typ TypeExpr) *ParamDecl {
return &ParamDecl{Name: name, Type: typ}
}
// StructDecl creates a struct declaration with the given fields/members.
func StructDecl(fields ...*ContainerMember) *ContainerDecl {
return &ContainerDecl{
Kind: ContainerStruct,
Fields: fields,
}
}
// Field creates a struct field (optionally with initializer).
func Field(name string, typ TypeExpr, byteAlign *Expr, value Expr) *ContainerMember {
return &ContainerMember{
Field: &ContainerField{
Name: name,
Type: typ,
ByteAlign: byteAlign,
Value: value,
},
}
}
// Method creates a method (function declaration) as a struct member.
func Method(fn *FnDecl) *ContainerMember {
return &ContainerMember{Decl: fn}
}
// OptionalType creates an optional type (?T).
func OptionalType(base TypeExpr) *PrefixTypeExpr {
return &PrefixTypeExpr{Op: "?", Base: base}
}
// PointerType creates a pointer type (*T).
func PointerType(base TypeExpr) *PrefixTypeExpr {
return &PrefixTypeExpr{Op: "*", Base: base}
}
// ArrayType creates an array type ([N]T).
func ArrayType(size Expr, elem TypeExpr) *ArrayTypeExpr {
return &ArrayTypeExpr{Size: size, Elem: elem}
}
// ErrorSet creates an error set declaration.
func ErrorSet(names ...string) *ErrorSetDecl {
return &ErrorSetDecl{Names: names}
}
// ErrorUnionType creates an error union type (E!T).
func ErrorUnionType(errSet, typ TypeExpr) *ErrorUnionTypeExpr {
return &ErrorUnionTypeExpr{ErrSet: errSet, Type: typ}
}
// DeferStmt creates a defer statement.
func Defer(stmt Stmt) *DeferStmt {
return &DeferStmt{Stmt: stmt}
}
// ForLoop creates a for loop statement.
func ForLoop(args []ForArg, payload *Payload, body Stmt, els Stmt) *LoopStmt {
return &LoopStmt{
Kind: LoopFor,
Prefix: &ForPrefix{Args: args, Payload: payload},
Body: body,
Else: els,
}
}
// WhileLoop creates a while loop statement.
func WhileLoop(cond Expr, continueExpr Expr, payload *Payload, body Stmt, els Stmt) *LoopStmt {
return &LoopStmt{
Kind: LoopWhile,
Prefix: &WhilePrefix{
Cond: cond,
Continue: continueExpr,
Payload: payload,
},
Body: body,
Else: els,
}
}
// ForArg creates a for loop argument.
func ForArgExpr(expr Expr) ForArg {
return ForArg{Expr: expr}
}
// Payload creates a payload for control flow.
func PayloadNames(names []string, pointers []bool) *Payload {
return &Payload{Names: names, Pointers: pointers}
}
// SwitchStmt creates a switch statement.
func Switch(cond Expr, prongs ...*SwitchProng) *SwitchStmt {
return &SwitchStmt{Cond: cond, Prongs: prongs}
}
// SwitchProng creates a switch prong.
func Prong(cases []*SwitchCase, payload *Payload, expr Expr) *SwitchProng {
return &SwitchProng{Cases: cases, Payload: payload, Expr: expr}
}
// SwitchCase creates a switch case.
func Case(expr Expr, to Expr) *SwitchCase {
return &SwitchCase{Expr: expr, To: to}
}
// ElseCase creates an else case for a switch.
func ElseCase() *SwitchCase {
return &SwitchCase{IsElse: true}
}
// BreakStmt creates a break statement.
func Break(label string, value Expr) *BreakStmt {
return &BreakStmt{Label: label, Value: value}
}
// ContinueStmt creates a continue statement.
func Continue(label string) *ContinueStmt {
return &ContinueStmt{Label: label}
}
// UnaryExpr creates a unary expression.
func Unary(op string, expr Expr) *UnaryExpr {
return &UnaryExpr{Op: op, Expr: expr}
}
// IndexExpr creates an index expression.
func Index(receiver, index Expr) *IndexExpr {
return &IndexExpr{Receiver: receiver, Index: index}
}
// UnreachableExpr creates an unreachable expression.
func Unreachable() *UnreachableExpr {
return &UnreachableExpr{}
}
// TestDecl creates a test declaration.
func Test(name string, block *Block) *TestDecl {
return &TestDecl{Name: name, Block: block}
}
// GroupedExpr creates a grouped (parenthesized) expression.
func Grouped(expr Expr) *GroupedExpr {
return &GroupedExpr{Expr: expr}
}
// ComptimeExpr creates a comptime expression.
func Comptime(expr Expr) *ComptimeExpr {
return &ComptimeExpr{Expr: expr}
}
// NosuspendExpr creates a nosuspend expression.
func Nosuspend(expr Expr) *NosuspendExpr {
return &NosuspendExpr{Expr: expr}
}
// SliceType creates a slice type ([]T).
func SliceType(elem TypeExpr) *SliceTypeExpr {
return &SliceTypeExpr{Elem: elem}
}
// IfExpression creates an if expression.
func IfExpression(cond Expr, then, els Expr) *IfExpr {
return &IfExpr{Cond: cond, Then: then, Else: els}
}
// IfExpressionWithPayload creates an if expression with a payload.
func IfExpressionWithPayload(cond Expr, payload *Payload, then, els Expr) *IfExpr {
return &IfExpr{Cond: cond, Payload: payload, Then: then, Else: els}
}
// SwitchExpression creates a switch expression.
func SwitchExpression(cond Expr, prongs ...*SwitchProng) *SwitchExpr {
return &SwitchExpr{Cond: cond, Prongs: prongs}
}
// EnumDecl creates an enum declaration.
func EnumDecl(tagType TypeExpr, fields ...*ContainerMember) *ContainerDecl {
return &ContainerDecl{
Kind: ContainerEnum,
TagType: tagType,
Fields: fields,
}
}
// UnionDecl creates a union declaration.
func UnionDecl(tagType TypeExpr, fields ...*ContainerMember) *ContainerDecl {
return &ContainerDecl{
Kind: ContainerUnion,
TagType: tagType,
Fields: fields,
}
}
// OpaqueDecl creates an opaque declaration.
func OpaqueDecl() *ContainerDecl {
return &ContainerDecl{
Kind: ContainerOpaque,
}
}
// AsyncExpr creates an async expression.
func Async(expr Expr) *AsyncExpr {
return &AsyncExpr{Expr: expr}
}
// AwaitExpr creates an await expression.
func Await(expr Expr) *AwaitExpr {
return &AwaitExpr{Expr: expr}
}
// ResumeExpr creates a resume expression.
func Resume(expr Expr) *ResumeExpr {
return &ResumeExpr{Expr: expr}
}
// DotAsterisk creates a .* expression (pointer dereference).
func DotAsterisk(receiver Expr) *DotAsteriskExpr {
return &DotAsteriskExpr{Receiver: receiver}
}
// DotQuestion creates a .? expression (optional unwrap).
func DotQuestion(receiver Expr) *DotQuestionExpr {
return &DotQuestionExpr{Receiver: receiver}
}
// ErrDefer creates an errdefer statement.
func ErrDefer(payload *Payload, stmt Stmt) *DeferStmt {
return &DeferStmt{ErrDefer: true, Payload: payload, Stmt: stmt}
}
// UsingNamespace creates a usingnamespace declaration.
func UsingNamespace(expr Expr) *UsingNamespaceDecl {
return &UsingNamespaceDecl{Expr: expr}
}
// ComptimeBlock creates a comptime block declaration.
func ComptimeBlock(block *Block) *ComptimeDecl {
return &ComptimeDecl{Block: block}
}
// InitListFields creates an init list with field initializers.
func InitListFields(fields ...*FieldInit) *InitListExpr {
return &InitListExpr{Fields: fields}
}
// FieldInitPair creates a field initializer.
func FieldInitPair(name string, value Expr) *FieldInit {
return &FieldInit{Name: name, Value: value}
}

View File

@@ -3,6 +3,7 @@ package zig
import ( import (
"fmt" "fmt"
"io" "io"
"strings"
) )
type formatter struct { type formatter struct {
@@ -15,9 +16,9 @@ type formatter struct {
// indentStr defines the string used for each indentation level (4 spaces). // indentStr defines the string used for each indentation level (4 spaces).
const indentStr = " " const indentStr = " "
// Writef writes formatted text to the underlying writer and updates line/col counters. // writef writes formatted text to the underlying writer and updates line/col counters.
// It also handles indentation after newlines when appropriate. // It also handles indentation after newlines when appropriate.
func (f *formatter) Writef(format string, a ...any) { func (f *formatter) writef(format string, a ...any) {
s := fmt.Sprintf(format, a...) s := fmt.Sprintf(format, a...)
for i, r := range s { for i, r := range s {
if r == '\n' { if r == '\n' {
@@ -62,37 +63,70 @@ func Write(w io.Writer, root *Root) (err error) {
} }
} }
}() }()
f := &formatter{w: w, line: 1, col: 1, indent: 0} sb := &strings.Builder{}
f := &formatter{w: sb, line: 1, col: 1, indent: 0}
if root.ContainerDocComment != "" { if root.ContainerDocComment != "" {
f.Writef("//! %s\n\n", root.ContainerDocComment) f.writef("//! %s\n\n", root.ContainerDocComment)
} }
for _, member := range root.ContainerMembers { for i, member := range root.ContainerMembers {
// Only handle Decl for now (fields not needed for hello world)
if member.Decl != nil { if member.Decl != nil {
// Only emit a leading newline before a function/global after the first declaration
if i > 0 {
f.writef("\n")
}
writeDecl(f, member.Decl) writeDecl(f, member.Decl)
} }
} }
return nil out := sb.String()
if len(out) == 0 || out[len(out)-1] != '\n' {
out += "\n"
}
_, err = w.Write([]byte(out))
return err
} }
// writeDecl emits a top-level declaration. // writeDecl emits a top-level declaration.
func writeDecl(f *formatter, decl Decl) { func writeDecl(f *formatter, decl Decl) {
switch d := decl.(type) { switch d := decl.(type) {
case *FnDecl: case *FnDecl:
f.Writef("\nfn %s(", d.Name) if d.Flags&FnExport != 0 {
f.writef("pub ")
}
f.writef("fn %s(", d.Name)
writeParams(f, d.Params) writeParams(f, d.Params)
f.Writef(") ") f.writef(") ")
writeTypeExpr(f, d.ReturnType) writeTypeExpr(f, d.ReturnType)
writeBlock(f, d.Body) writeBlock(f, d.Body)
f.writef("\n")
case *GlobalVarDecl: case *GlobalVarDecl:
if d.Const { if d.Flags&GlobalVarConst != 0 {
f.Writef("const %s = ", d.Name) f.writef("const %s = ", d.Name)
} else { } else {
f.Writef("var %s = ", d.Name) f.writef("var %s = ", d.Name)
} }
writeExpr(f, d.Value) writeExpr(f, d.Value)
f.Writef(";\n") f.writef(";\n")
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")
case *UsingNamespaceDecl:
f.writef("usingnamespace ")
writeExpr(f, d.Expr)
f.writef(";\n")
case *ComptimeDecl:
f.writef("comptime")
writeBlock(f, d.Block)
f.writef("\n")
} }
} }
@@ -100,10 +134,10 @@ func writeDecl(f *formatter, decl Decl) {
func writeParams(f *formatter, params []*ParamDecl) { func writeParams(f *formatter, params []*ParamDecl) {
for i, param := range params { for i, param := range params {
if i > 0 { if i > 0 {
f.Writef(", ") f.writef(", ")
} }
if param.Name != "" { if param.Name != "" {
f.Writef("%s: ", param.Name) f.writef("%s: ", param.Name)
} }
writeTypeExpr(f, param.Type) writeTypeExpr(f, param.Type)
} }
@@ -113,48 +147,332 @@ func writeParams(f *formatter, params []*ParamDecl) {
func writeTypeExpr(f *formatter, typ TypeExpr) { func writeTypeExpr(f *formatter, typ TypeExpr) {
switch t := typ.(type) { switch t := typ.(type) {
case *Identifier: case *Identifier:
f.Writef("%s", t.Name) 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("[")
writeExpr(f, t.Size)
f.writef("]")
writeTypeExpr(f, t.Elem)
case *SliceTypeExpr:
f.writef("[]")
writeTypeExpr(f, t.Elem)
case *ErrorUnionTypeExpr:
writeTypeExpr(f, t.ErrSet)
f.writef("!")
writeTypeExpr(f, t.Type)
case nil: case nil:
// nothing // nothing
default: default:
f.Writef("%v", t) f.writef("%v", t)
} }
} }
// writeBlock emits a block, handling indentation for statements and the closing brace. // writeBlock emits a block, handling indentation for statements and the closing brace.
func writeBlock(f *formatter, block *Block) { func writeBlock(f *formatter, block *Block) {
if block == nil { if block == nil {
f.Writef(";") f.writef(";")
return return
} }
f.Writef(" {\n") f.writef(" {\n")
f.indent++ // Increase indentation for block contents. f.indent++
for i, stmt := range block.Stmts { for _, stmt := range block.Stmts {
f.writeIndent() // Indent each statement. f.writeIndent()
writeStmt(f, stmt) writeStmt(f, stmt)
if i < len(block.Stmts)-1 { f.writef("\n")
f.Writef("\n")
}
} }
f.indent-- // Decrease indentation before closing brace. f.indent--
f.Writef("\n") f.writeIndent()
f.writeIndent() // Indent the closing brace. f.writef("}")
f.Writef("}\n")
} }
// writeStmt emits a statement. Indentation is handled by the caller (writeBlock). // writeStmt emits a statement. Indentation is handled by the caller (writeBlock).
func writeStmt(f *formatter, stmt Stmt) { func writeStmt(f *formatter, stmt Stmt) {
switch s := stmt.(type) { switch s := stmt.(type) {
case *ReturnStmt: case *ReturnStmt:
f.Writef("return") f.writef("return")
if s.Value != nil { if s.Value != nil {
f.Writef(" ") f.writef(" ")
writeExpr(f, s.Value) writeExpr(f, s.Value)
} }
f.Writef(";") f.writef(";")
case *ExprStmt: case *ExprStmt:
writeExpr(f, s.Expr) writeExpr(f, s.Expr)
f.Writef(";") f.writef(";")
case *VarDeclStmt:
if s.Const {
f.writef("const ")
} else {
f.writef("var ")
}
for i, name := range s.Pattern.Names {
if i > 0 {
f.writef(", ")
}
f.writef("%s", name)
}
if s.Type != nil {
f.writef(": ")
writeTypeExpr(f, s.Type)
}
if s.Value != nil {
f.writef(" = ")
writeExpr(f, s.Value)
}
f.writef(";")
case *BlockStmt:
// 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)
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 {
writeBlock(f, block.Block)
} else {
f.writef(" ")
writeStmt(f, s.Then)
}
if s.Else != nil {
f.writef(" else")
if block, ok := s.Else.(*BlockStmt); ok {
writeBlock(f, block.Block)
} else {
f.writef(" ")
writeStmt(f, s.Else)
}
}
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 {
writeExpr(f, wp.Cond)
if wp.Continue != nil {
f.writef(") : (")
writeExpr(f, wp.Continue)
}
}
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)
}
} else if s.Kind == LoopFor {
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 {
// 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)
}
}
case *DeferStmt:
if s.ErrDefer {
f.writef("errdefer")
if s.Payload != nil {
f.writef(" ")
writePayload(f, s.Payload)
}
} 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(" => ")
// 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
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(",")
} }
} }
@@ -162,34 +480,215 @@ func writeStmt(f *formatter, stmt Stmt) {
func writeExpr(f *formatter, expr Expr) { func writeExpr(f *formatter, expr Expr) {
switch e := expr.(type) { switch e := expr.(type) {
case *Identifier: case *Identifier:
f.Writef("%s", e.Name) f.writef("%s", e.Name)
case *CallExpr: case *CallExpr:
writeExpr(f, e.Fun) writeExpr(f, e.Fun)
f.Writef("(") f.writef("(")
for i, arg := range e.Args { for i, arg := range e.Args {
if i > 0 { if i > 0 {
f.Writef(", ") f.writef(", ")
} }
writeExpr(f, arg) writeExpr(f, arg)
} }
f.Writef(")") f.writef(")")
case *FieldAccessExpr: case *FieldAccessExpr:
writeExpr(f, e.Receiver) // Handle enum field access like .value (no receiver)
f.Writef(".%s", e.Field) 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: case *Literal:
switch e.Kind { switch e.Kind {
case "string": case LiteralString:
f.Writef("%q", e.Value) f.writef(`"%v"`, e.Value)
default: default:
f.Writef("%v", e.Value) f.writef("%v", e.Value)
} }
case *InitListExpr: case *InitListExpr:
if e.Empty { if e.Empty {
f.Writef(".{}") f.writef(".{}")
} else { } else if len(e.Values) > 0 {
f.Writef(".{") if len(e.Values) == 1 {
// TODO f.writef(".{")
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)
}
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 {
case ContainerStruct:
f.writef("struct ")
writeStructBody(f, e)
case ContainerEnum:
f.writef("enum ")
writeStructBody(f, e)
case ContainerUnion:
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 ")
writeStructBody(f, e)
default:
panic("unknown container kind")
}
case *TryExpr:
f.writef("try ")
writeExpr(f, e.Expr)
case *BinaryExpr:
writeExpr(f, e.Left)
f.writef(" %s ", e.Op)
// 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)
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("}")
case *GroupedExpr:
f.writef("(")
writeExpr(f, e.Expr)
f.writef(")")
case *ComptimeExpr:
f.writef("comptime ")
writeExpr(f, e.Expr)
case *NosuspendExpr:
f.writef("nosuspend ")
writeExpr(f, e.Expr)
case *AsyncExpr:
f.writef("async ")
writeExpr(f, e.Expr)
case *AwaitExpr:
f.writef("await ")
writeExpr(f, e.Expr)
case *ResumeExpr:
f.writef("resume ")
writeExpr(f, e.Expr)
case *DotAsteriskExpr:
writeExpr(f, e.Receiver)
f.writef(".*")
case *DotQuestionExpr:
writeExpr(f, e.Receiver)
f.writef(".?")
case *IfExpr:
f.writef("if (")
writeExpr(f, e.Cond)
f.writef(")")
if e.Payload != nil {
f.writef(" ")
writePayload(f, e.Payload)
}
f.writef(" ")
writeExpr(f, e.Then)
if e.Else != nil {
f.writef(" else ")
writeExpr(f, e.Else)
}
case *SwitchExpr:
f.writef("switch (")
writeExpr(f, e.Cond)
f.writef(") {\n")
f.indent++
for _, prong := range e.Prongs {
f.writeIndent()
writeSwitchProng(f, prong)
f.writef("\n")
}
f.indent--
f.writeIndent()
f.writef("}")
case *LabeledBlock:
f.writef("%s:", e.Label)
writeBlock(f, e.Block)
} }
} }
// writeStructBody emits the body of a struct/union/enum/opaque declaration.
func writeStructBody(f *formatter, decl *ContainerDecl) {
f.writef("{\n")
f.indent++
for _, member := range decl.Fields {
if member.Field != nil {
// Field or const
if member.Field.Type == nil && member.Field.Value != nil {
// const field
f.writeIndent()
f.writef("const %s = ", member.Field.Name)
writeExpr(f, member.Field.Value)
f.writef(";\n")
} else {
// regular field
f.writeIndent()
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)
}
f.writef(",\n")
}
} else if member.Decl != nil {
// Method or nested decl
f.writef("\n")
f.writeIndent()
writeDecl(f, member.Decl)
}
}
f.indent--
f.writeIndent()
f.writef("}")
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
Hello, world!

View File

@@ -1,5 +1,5 @@
package main package main
func main() { func main() {
print("Hello, world\n") print("Hello, world!\n")
} }

View File

@@ -0,0 +1 @@
First Second

View File

@@ -0,0 +1,8 @@
package main
func main() {
print("First")
print(" ")
print("Second")
print("\n")
}

View File

@@ -0,0 +1,5 @@
Hello World!
Line 1
Line 2
Quote: "test"
Backslash: \

View File

@@ -0,0 +1,8 @@
package main
func main() {
print("Hello\tWorld!\n")
print("Line 1\nLine 2\n")
print("Quote: \"test\"\n")
print("Backslash: \\\n")
}