Compare commits
No commits in common. "bea04d681025d64643526321c345bc51db626448" and "50b38254ab2e5b1418364d65a4969f230b8c2c83" have entirely different histories.
bea04d6810
...
50b38254ab
12
Makefile
12
Makefile
@ -1,15 +1,5 @@
|
|||||||
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-unit test-integration
|
test:
|
||||||
|
|
||||||
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
|
|
||||||
|
@ -1,228 +0,0 @@
|
|||||||
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")
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() void {
|
|
||||||
std.debug.print("Hello, world\n", .{});
|
|
||||||
}
|
|
133
internal/main.go
133
internal/main.go
@ -8,8 +8,6 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.frop.prof/luke/go-zig-compiler/internal/zig"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -38,122 +36,57 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
zigRoot, err := translateToZig(f)
|
output, err := generate(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)
|
||||||
}
|
}
|
||||||
defer outputFile.Close()
|
_, err = outputFile.WriteString(output)
|
||||||
|
|
||||||
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 translateToZig(f *ast.File) (*zig.Root, error) {
|
func generate(f *ast.File) (string, error) {
|
||||||
// Create the root AST node
|
sb := new(strings.Builder)
|
||||||
root := &zig.Root{
|
|
||||||
ContainerMembers: []*zig.ContainerMember{},
|
def := f.Decls[0].(*ast.FuncDecl)
|
||||||
|
|
||||||
|
if def.Name.Name != "main" {
|
||||||
|
return "", fmt.Errorf("must have main")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the std import
|
sb.WriteString(`const std = @import("std");`)
|
||||||
root.ContainerMembers = append(root.ContainerMembers, &zig.ContainerMember{
|
sb.WriteString("\npub fn main() void {\n")
|
||||||
Decl: zig.DeclareGlobalVar("std",
|
|
||||||
zig.Call(zig.Id("@import"), zig.StringLit("std")),
|
|
||||||
zig.GlobalVarConst,
|
|
||||||
),
|
|
||||||
})
|
|
||||||
|
|
||||||
// Find and translate the main function
|
stmt := def.Body.List[0].(*ast.ExprStmt)
|
||||||
for _, decl := range f.Decls {
|
call := stmt.X.(*ast.CallExpr)
|
||||||
if fn, ok := decl.(*ast.FuncDecl); ok && fn.Name.Name == "main" {
|
fn := call.Fun.(*ast.Ident)
|
||||||
mainFunc, err := translateMainFunction(fn)
|
|
||||||
if err != nil {
|
if fn.Name == "print" {
|
||||||
return nil, err
|
sb.WriteString(fmt.Sprintf(`std.debug.print(`))
|
||||||
|
|
||||||
|
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")
|
||||||
}
|
}
|
||||||
|
|
||||||
return root, nil
|
sb.WriteString("}\n")
|
||||||
}
|
|
||||||
|
|
||||||
func translateMainFunction(fn *ast.FuncDecl) (*zig.FnDecl, error) {
|
return sb.String(), nil
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,9 @@
|
|||||||
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 // Module-level doc comment using //!
|
ContainerDocComment DocComment // //! Doc Comment (optional)
|
||||||
ContainerMembers []*ContainerMember // Top-level declarations and fields
|
ContainerMembers []*ContainerMember
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerMember struct {
|
type ContainerMember struct {
|
||||||
@ -112,21 +111,11 @@ 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.
|
// ContainerDecl represents a struct, enum, union, or opaque declaration.
|
||||||
type ContainerDecl struct {
|
type ContainerDecl struct {
|
||||||
Extern bool
|
Extern bool
|
||||||
Packed bool
|
Packed bool
|
||||||
Kind ContainerKind
|
Kind string // "struct", "enum", "union", "opaque"
|
||||||
TagType TypeExpr // Optional (for enum/union)
|
TagType TypeExpr // Optional (for enum/union)
|
||||||
Fields []*ContainerMember
|
Fields []*ContainerMember
|
||||||
DocComment DocComment
|
DocComment DocComment
|
||||||
@ -237,18 +226,10 @@ 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 LoopKind // For or While
|
Kind string // "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
|
||||||
@ -350,19 +331,9 @@ 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 LiteralKind
|
Kind string // "int", "float", "string", "char"
|
||||||
Value string // The literal value as a string
|
Value string // The literal value as a string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,14 +391,15 @@ 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.
|
// PrefixTypeExpr represents a type with a string prefix. Examples include optionals and pointers.
|
||||||
@ -436,23 +408,6 @@ type PrefixTypeExpr struct {
|
|||||||
Base TypeExpr
|
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
|
||||||
@ -528,14 +483,43 @@ type AwaitExpr struct {
|
|||||||
// UnreachableExpr represents the 'unreachable' keyword.
|
// UnreachableExpr represents the 'unreachable' keyword.
|
||||||
type UnreachableExpr struct{}
|
type UnreachableExpr struct{}
|
||||||
|
|
||||||
// Note: The following types were removed as they were duplicates:
|
// EmptyInitListExpr represents an empty initializer list '{}'.
|
||||||
// - EmptyInitListExpr (use InitListExpr with Empty=true)
|
type EmptyInitListExpr struct{}
|
||||||
// - PositionalInitListExpr (use InitListExpr with Values)
|
|
||||||
// - FieldInitListExpr (use InitListExpr with Fields)
|
// PositionalInitListExpr represents a positional initializer list '{expr, ...}'.
|
||||||
// - SwitchProngPayload (use Payload)
|
type PositionalInitListExpr struct {
|
||||||
// - SwitchProngCase (use SwitchCase)
|
Values []Expr // Expressions in order
|
||||||
// - 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 {
|
||||||
|
@ -5,24 +5,9 @@ func Id(name string) *Identifier {
|
|||||||
return &Identifier{Name: name}
|
return &Identifier{Name: name}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntLit creates an integer literal.
|
// Lit creates a literal expression.
|
||||||
func IntLit(value string) *Literal {
|
func Lit(kind, value string) *Literal {
|
||||||
return &Literal{Kind: LiteralInt, Value: value}
|
return &Literal{Kind: kind, 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.
|
// FieldAccess creates a field access expression.
|
||||||
@ -88,11 +73,6 @@ func If(cond Expr, then, els Stmt) *IfStmt {
|
|||||||
return &IfStmt{Cond: cond, Then: then, Else: els}
|
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.
|
// NewBlock creates a block of statements.
|
||||||
func NewBlock(stmts ...Stmt) *Block {
|
func NewBlock(stmts ...Stmt) *Block {
|
||||||
return &Block{Stmts: stmts}
|
return &Block{Stmts: stmts}
|
||||||
@ -123,7 +103,7 @@ func Param(name string, typ TypeExpr) *ParamDecl {
|
|||||||
// StructDecl creates a struct declaration with the given fields/members.
|
// StructDecl creates a struct declaration with the given fields/members.
|
||||||
func StructDecl(fields ...*ContainerMember) *ContainerDecl {
|
func StructDecl(fields ...*ContainerMember) *ContainerDecl {
|
||||||
return &ContainerDecl{
|
return &ContainerDecl{
|
||||||
Kind: ContainerStruct,
|
Kind: "struct",
|
||||||
Fields: fields,
|
Fields: fields,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,217 +134,3 @@ func OptionalType(base TypeExpr) *PrefixTypeExpr {
|
|||||||
func PointerType(base TypeExpr) *PrefixTypeExpr {
|
func PointerType(base TypeExpr) *PrefixTypeExpr {
|
||||||
return &PrefixTypeExpr{Op: "*", Base: base}
|
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}
|
|
||||||
}
|
|
||||||
|
@ -98,7 +98,6 @@ func writeDecl(f *formatter, decl Decl) {
|
|||||||
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.Flags&GlobalVarConst != 0 {
|
if d.Flags&GlobalVarConst != 0 {
|
||||||
f.writef("const %s = ", d.Name)
|
f.writef("const %s = ", d.Name)
|
||||||
@ -110,23 +109,6 @@ func writeDecl(f *formatter, decl Decl) {
|
|||||||
case *ContainerDecl:
|
case *ContainerDecl:
|
||||||
f.writef("struct ")
|
f.writef("struct ")
|
||||||
writeStructBody(f, d)
|
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")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,23 +132,7 @@ func writeTypeExpr(f *formatter, typ TypeExpr) {
|
|||||||
f.writef("%s", t.Name)
|
f.writef("%s", t.Name)
|
||||||
case *PrefixTypeExpr:
|
case *PrefixTypeExpr:
|
||||||
f.writef("%s", t.Op)
|
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)
|
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:
|
||||||
@ -227,36 +193,12 @@ func writeStmt(f *formatter, stmt Stmt) {
|
|||||||
}
|
}
|
||||||
f.writef(";")
|
f.writef(";")
|
||||||
case *BlockStmt:
|
case *BlockStmt:
|
||||||
// Check if this is a labeled block
|
writeBlock(f, s.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:
|
case *IfStmt:
|
||||||
f.writef("if (")
|
f.writef("if (")
|
||||||
writeExpr(f, s.Cond)
|
writeExpr(f, s.Cond)
|
||||||
f.writef(")")
|
f.writef(")")
|
||||||
|
|
||||||
// Handle payload if present
|
|
||||||
if s.Payload != nil {
|
|
||||||
f.writef(" ")
|
|
||||||
writePayload(f, s.Payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always write the then branch as a block
|
// Always write the then branch as a block
|
||||||
if block, ok := s.Then.(*BlockStmt); ok {
|
if block, ok := s.Then.(*BlockStmt); ok {
|
||||||
writeBlock(f, block.Block)
|
writeBlock(f, block.Block)
|
||||||
@ -274,12 +216,7 @@ func writeStmt(f *formatter, stmt Stmt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *LoopStmt:
|
case *LoopStmt:
|
||||||
// Handle label if the body is a labeled block
|
if s.Kind == "while" {
|
||||||
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 (")
|
f.writef("while (")
|
||||||
if wp, ok := s.Prefix.(*WhilePrefix); ok {
|
if wp, ok := s.Prefix.(*WhilePrefix); ok {
|
||||||
writeExpr(f, wp.Cond)
|
writeExpr(f, wp.Cond)
|
||||||
@ -291,188 +228,12 @@ func writeStmt(f *formatter, stmt Stmt) {
|
|||||||
f.writef(")")
|
f.writef(")")
|
||||||
// Always write the body as a block
|
// Always write the body as a block
|
||||||
if block, ok := s.Body.(*BlockStmt); ok {
|
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)
|
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 {
|
} else {
|
||||||
f.writef(" ")
|
f.writef(" ")
|
||||||
writeStmt(f, s.Body)
|
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(",")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,16 +253,11 @@ func writeExpr(f *formatter, expr Expr) {
|
|||||||
}
|
}
|
||||||
f.writef(")")
|
f.writef(")")
|
||||||
case *FieldAccessExpr:
|
case *FieldAccessExpr:
|
||||||
// Handle enum field access like .value (no receiver)
|
writeExpr(f, e.Receiver)
|
||||||
if id, ok := e.Receiver.(*Identifier); ok && id.Name == "" {
|
f.writef(".%s", e.Field)
|
||||||
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 LiteralString:
|
case "string":
|
||||||
f.writef(`"%v"`, e.Value)
|
f.writef(`"%v"`, e.Value)
|
||||||
default:
|
default:
|
||||||
f.writef("%v", e.Value)
|
f.writef("%v", e.Value)
|
||||||
@ -510,54 +266,21 @@ func writeExpr(f *formatter, expr Expr) {
|
|||||||
if e.Empty {
|
if e.Empty {
|
||||||
f.writef(".{}")
|
f.writef(".{}")
|
||||||
} else if len(e.Values) > 0 {
|
} else if len(e.Values) > 0 {
|
||||||
if len(e.Values) == 1 {
|
f.writef(".{")
|
||||||
f.writef(".{")
|
for i, v := range e.Values {
|
||||||
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 {
|
if i > 0 {
|
||||||
f.writef(", ")
|
f.writef(", ")
|
||||||
}
|
}
|
||||||
f.writef(".%s = ", field.Name)
|
writeExpr(f, v)
|
||||||
writeExpr(f, field.Value)
|
|
||||||
}
|
}
|
||||||
f.writef(" }")
|
f.writef("}")
|
||||||
}
|
}
|
||||||
case *ContainerDecl:
|
case *ContainerDecl:
|
||||||
switch e.Kind {
|
if e.Kind == "struct" {
|
||||||
case ContainerStruct:
|
|
||||||
f.writef("struct ")
|
f.writef("struct ")
|
||||||
writeStructBody(f, e)
|
writeStructBody(f, e)
|
||||||
case ContainerEnum:
|
} else {
|
||||||
f.writef("enum ")
|
panic("not implemented: " + e.Kind)
|
||||||
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:
|
case *TryExpr:
|
||||||
f.writef("try ")
|
f.writef("try ")
|
||||||
@ -565,92 +288,7 @@ func writeExpr(f *formatter, expr Expr) {
|
|||||||
case *BinaryExpr:
|
case *BinaryExpr:
|
||||||
writeExpr(f, e.Left)
|
writeExpr(f, e.Left)
|
||||||
f.writef(" %s ", e.Op)
|
f.writef(" %s ", e.Op)
|
||||||
// Special handling for orelse with block
|
writeExpr(f, e.Right)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,11 +308,8 @@ func writeStructBody(f *formatter, decl *ContainerDecl) {
|
|||||||
} else {
|
} else {
|
||||||
// regular field
|
// regular field
|
||||||
f.writeIndent()
|
f.writeIndent()
|
||||||
f.writef("%s", member.Field.Name)
|
f.writef("%s: ", member.Field.Name)
|
||||||
if member.Field.Type != nil {
|
writeTypeExpr(f, member.Field.Type)
|
||||||
f.writef(": ")
|
|
||||||
writeTypeExpr(f, member.Field.Type)
|
|
||||||
}
|
|
||||||
if member.Field.Value != nil {
|
if member.Field.Value != nil {
|
||||||
f.writef(" = ")
|
f.writef(" = ")
|
||||||
writeExpr(f, member.Field.Value)
|
writeExpr(f, member.Field.Value)
|
||||||
@ -686,6 +321,7 @@ func writeStructBody(f *formatter, decl *ContainerDecl) {
|
|||||||
f.writef("\n")
|
f.writef("\n")
|
||||||
f.writeIndent()
|
f.writeIndent()
|
||||||
writeDecl(f, member.Decl)
|
writeDecl(f, member.Decl)
|
||||||
|
f.writef("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.indent--
|
f.indent--
|
||||||
|
@ -63,7 +63,7 @@ fn main() void {
|
|||||||
{
|
{
|
||||||
Decl: zig.DeclareGlobalVar("std", zig.Call(
|
Decl: zig.DeclareGlobalVar("std", zig.Call(
|
||||||
zig.Id("@import"),
|
zig.Id("@import"),
|
||||||
zig.StringLit("std"),
|
zig.Lit("string", "std"),
|
||||||
), zig.GlobalVarConst),
|
), zig.GlobalVarConst),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -77,7 +77,7 @@ fn main() void {
|
|||||||
zig.FieldAccess(zig.Id("std"), "debug"),
|
zig.FieldAccess(zig.Id("std"), "debug"),
|
||||||
"print",
|
"print",
|
||||||
),
|
),
|
||||||
zig.StringLit(`Hello, world!\n`),
|
zig.Lit("string", `Hello, world!\n`),
|
||||||
zig.InitList(),
|
zig.InitList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -92,217 +92,6 @@ fn main() void {
|
|||||||
runZigASTTest(t, expected, root)
|
runZigASTTest(t, expected, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompoundFeatures(t *testing.T) {
|
|
||||||
expected := `const ProcessError = error{
|
|
||||||
InvalidInput,
|
|
||||||
OutOfBounds,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn processData(values: [5]?i32) ProcessError!i32 {
|
|
||||||
var result: i32 = 0;
|
|
||||||
defer std.debug.print("Cleanup\n", .{});
|
|
||||||
for (values, 0..) |opt_val, idx| {
|
|
||||||
if (opt_val) |val| {
|
|
||||||
switch (val) {
|
|
||||||
-10...-1 => result += -val,
|
|
||||||
0 => continue,
|
|
||||||
1...10 => {
|
|
||||||
if (idx > 3) break;
|
|
||||||
result += val;
|
|
||||||
},
|
|
||||||
else => return ProcessError.InvalidInput,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (idx == 0) return ProcessError.InvalidInput;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (values[0] == null) {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
test "processData" {
|
|
||||||
const data = .{ 5, -3, 0, null, 10 };
|
|
||||||
const result = try processData(data);
|
|
||||||
try std.testing.expectEqual(@as(i32, 8), result);
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
root := &zig.Root{
|
|
||||||
ContainerMembers: []*zig.ContainerMember{
|
|
||||||
{
|
|
||||||
Decl: zig.DeclareGlobalVar("ProcessError",
|
|
||||||
zig.ErrorSet("InvalidInput", "OutOfBounds"),
|
|
||||||
zig.GlobalVarConst,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Decl: zig.DeclareFn(
|
|
||||||
"processData",
|
|
||||||
zig.ErrorUnionType(zig.Id("ProcessError"), zig.Id("i32")),
|
|
||||||
zig.NewBlock(
|
|
||||||
// var result: i32 = 0;
|
|
||||||
zig.DeclareVarStmt(false, []string{"result"}, zig.Id("i32"), zig.IntLit("0")),
|
|
||||||
// defer std.debug.print("Cleanup\n", .{});
|
|
||||||
zig.Defer(
|
|
||||||
zig.NewExprStmt(
|
|
||||||
zig.Call(
|
|
||||||
zig.FieldAccess(
|
|
||||||
zig.FieldAccess(zig.Id("std"), "debug"),
|
|
||||||
"print",
|
|
||||||
),
|
|
||||||
zig.StringLit(`Cleanup\n`),
|
|
||||||
zig.InitList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// for (values, 0..) |opt_val, idx| { ... }
|
|
||||||
zig.ForLoop(
|
|
||||||
[]zig.ForArg{
|
|
||||||
zig.ForArgExpr(zig.Id("values")),
|
|
||||||
{Expr: zig.IntLit("0"), From: zig.IntLit("")},
|
|
||||||
},
|
|
||||||
zig.PayloadNames([]string{"opt_val", "idx"}, []bool{false, false}),
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
// if (opt_val) |val| { ... } else { ... }
|
|
||||||
zig.IfWithPayload(
|
|
||||||
zig.Id("opt_val"),
|
|
||||||
zig.PayloadNames([]string{"val"}, []bool{false}),
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
// switch (val) { ... }
|
|
||||||
zig.Switch(
|
|
||||||
zig.Id("val"),
|
|
||||||
// -10...-1 => result += -val,
|
|
||||||
zig.Prong(
|
|
||||||
[]*zig.SwitchCase{
|
|
||||||
zig.Case(
|
|
||||||
zig.Unary("-", zig.IntLit("10")),
|
|
||||||
zig.Unary("-", zig.IntLit("1")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
zig.NewExprStmt(
|
|
||||||
zig.Binary("+=",
|
|
||||||
zig.Id("result"),
|
|
||||||
zig.Unary("-", zig.Id("val")),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// 0 => continue,
|
|
||||||
zig.Prong(
|
|
||||||
[]*zig.SwitchCase{
|
|
||||||
zig.Case(zig.IntLit("0"), nil),
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
zig.Continue(""),
|
|
||||||
),
|
|
||||||
// 1...10 => { ... }
|
|
||||||
zig.Prong(
|
|
||||||
[]*zig.SwitchCase{
|
|
||||||
zig.Case(zig.IntLit("1"), zig.IntLit("10")),
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.If(
|
|
||||||
zig.Binary(">", zig.Id("idx"), zig.IntLit("3")),
|
|
||||||
zig.Break("", nil),
|
|
||||||
nil,
|
|
||||||
),
|
|
||||||
zig.NewExprStmt(
|
|
||||||
zig.Binary("+=", zig.Id("result"), zig.Id("val")),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// else => return ProcessError.InvalidInput,
|
|
||||||
zig.Prong(
|
|
||||||
[]*zig.SwitchCase{zig.ElseCase()},
|
|
||||||
nil,
|
|
||||||
zig.Return(
|
|
||||||
zig.FieldAccess(zig.Id("ProcessError"), "InvalidInput"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// else block
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.If(
|
|
||||||
zig.Binary("==", zig.Id("idx"), zig.IntLit("0")),
|
|
||||||
zig.Return(zig.FieldAccess(zig.Id("ProcessError"), "InvalidInput")),
|
|
||||||
nil,
|
|
||||||
),
|
|
||||||
zig.Break("", nil),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
nil,
|
|
||||||
),
|
|
||||||
// if (values[0] == null) { unreachable; }
|
|
||||||
zig.If(
|
|
||||||
zig.Binary("==",
|
|
||||||
zig.Index(zig.Id("values"), zig.IntLit("0")),
|
|
||||||
zig.Id("null"),
|
|
||||||
),
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.NewExprStmt(zig.Unreachable()),
|
|
||||||
),
|
|
||||||
nil,
|
|
||||||
),
|
|
||||||
// return result;
|
|
||||||
zig.Return(zig.Id("result")),
|
|
||||||
),
|
|
||||||
[]*zig.ParamDecl{
|
|
||||||
zig.Param("values", zig.ArrayType(zig.IntLit("5"), zig.OptionalType(zig.Id("i32")))),
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Decl: zig.Test("processData",
|
|
||||||
zig.NewBlock(
|
|
||||||
// const data = [_]?i32{ 5, -3, 0, null, 10 };
|
|
||||||
zig.DeclareVarStmt(true, []string{"data"}, nil,
|
|
||||||
&zig.InitListExpr{
|
|
||||||
Values: []zig.Expr{
|
|
||||||
zig.IntLit("5"),
|
|
||||||
zig.Unary("-", zig.IntLit("3")),
|
|
||||||
zig.IntLit("0"),
|
|
||||||
zig.Id("null"),
|
|
||||||
zig.IntLit("10"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// const result = try processData(data);
|
|
||||||
zig.DeclareVarStmt(true, []string{"result"}, nil,
|
|
||||||
zig.Try(zig.Call(zig.Id("processData"), zig.Id("data"))),
|
|
||||||
),
|
|
||||||
// try std.testing.expectEqual(@as(i32, 8), result);
|
|
||||||
zig.NewExprStmt(
|
|
||||||
zig.Try(
|
|
||||||
zig.Call(
|
|
||||||
zig.FieldAccess(
|
|
||||||
zig.FieldAccess(zig.Id("std"), "testing"),
|
|
||||||
"expectEqual",
|
|
||||||
),
|
|
||||||
zig.Call(
|
|
||||||
zig.Id("@as"),
|
|
||||||
zig.Id("i32"),
|
|
||||||
zig.IntLit("8"),
|
|
||||||
),
|
|
||||||
zig.Id("result"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
runZigASTTest(t, expected, root)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEvenOdd(t *testing.T) {
|
func TestEvenOdd(t *testing.T) {
|
||||||
expected := `//! Abc
|
expected := `//! Abc
|
||||||
|
|
||||||
@ -328,7 +117,7 @@ pub fn main() !void {
|
|||||||
Decl: zig.DeclareGlobalVar("std",
|
Decl: zig.DeclareGlobalVar("std",
|
||||||
zig.Call(
|
zig.Call(
|
||||||
zig.Id("@import"),
|
zig.Id("@import"),
|
||||||
zig.StringLit("std"),
|
zig.Lit("string", "std"),
|
||||||
),
|
),
|
||||||
zig.GlobalVarConst,
|
zig.GlobalVarConst,
|
||||||
),
|
),
|
||||||
@ -353,24 +142,26 @@ pub fn main() !void {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
// var i: i32 = 1;
|
// var i: i32 = 1;
|
||||||
zig.DeclareVarStmt(false, []string{"i"}, zig.Id("i32"), zig.IntLit("1")),
|
zig.DeclareVarStmt(false, []string{"i"}, zig.Id("i32"), zig.Lit("int", "1")),
|
||||||
// while (i <= 5) : (i += 1) { ... }
|
// while (i <= 5) : (i += 1) { ... }
|
||||||
zig.WhileLoop(
|
&zig.LoopStmt{
|
||||||
zig.Binary("<=", zig.Id("i"), zig.IntLit("5")),
|
Kind: "while",
|
||||||
zig.Binary("+=", zig.Id("i"), zig.IntLit("1")),
|
Prefix: &zig.WhilePrefix{
|
||||||
nil,
|
Cond: zig.Binary("<=", zig.Id("i"), zig.Lit("int", "5")),
|
||||||
zig.NewBlockStmt(
|
Continue: zig.Binary("+=", zig.Id("i"), zig.Lit("int", "1")),
|
||||||
|
},
|
||||||
|
Body: zig.NewBlockStmt(
|
||||||
&zig.IfStmt{
|
&zig.IfStmt{
|
||||||
Cond: zig.Binary("==",
|
Cond: zig.Binary("==",
|
||||||
zig.Binary("%", zig.Id("i"), zig.IntLit("2")),
|
zig.Binary("%", zig.Id("i"), zig.Lit("int", "2")),
|
||||||
zig.IntLit("0"),
|
zig.Lit("int", "0"),
|
||||||
),
|
),
|
||||||
Then: zig.NewBlockStmt(
|
Then: zig.NewBlockStmt(
|
||||||
zig.NewExprStmt(
|
zig.NewExprStmt(
|
||||||
zig.Try(
|
zig.Try(
|
||||||
zig.Call(
|
zig.Call(
|
||||||
zig.FieldAccess(zig.Id("stdout"), "writeAll"),
|
zig.FieldAccess(zig.Id("stdout"), "writeAll"),
|
||||||
zig.StringLit(`even: {d}\n`),
|
zig.Lit("string", `even: {d}\n`),
|
||||||
zig.InitList(zig.Id("i")),
|
zig.InitList(zig.Id("i")),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -381,7 +172,7 @@ pub fn main() !void {
|
|||||||
zig.Try(
|
zig.Try(
|
||||||
zig.Call(
|
zig.Call(
|
||||||
zig.FieldAccess(zig.Id("stdout"), "writeAll"),
|
zig.FieldAccess(zig.Id("stdout"), "writeAll"),
|
||||||
zig.StringLit(`odd: {d}\n`),
|
zig.Lit("string", `odd: {d}\n`),
|
||||||
zig.InitList(zig.Id("i")),
|
zig.InitList(zig.Id("i")),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -389,8 +180,7 @@ pub fn main() !void {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
nil,
|
},
|
||||||
),
|
|
||||||
),
|
),
|
||||||
nil,
|
nil,
|
||||||
zig.FnExport,
|
zig.FnExport,
|
||||||
@ -429,7 +219,7 @@ func TestStructWithFieldsAndMethod(t *testing.T) {
|
|||||||
zig.NewExprStmt(
|
zig.NewExprStmt(
|
||||||
zig.Binary("=",
|
zig.Binary("=",
|
||||||
zig.FieldAccess(zig.Id("self"), "count"),
|
zig.FieldAccess(zig.Id("self"), "count"),
|
||||||
zig.IntLit("0"),
|
zig.Lit("int", "0"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -447,643 +237,3 @@ func TestStructWithFieldsAndMethod(t *testing.T) {
|
|||||||
|
|
||||||
runZigASTTest(t, expected, root)
|
runZigASTTest(t, expected, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAllExpressionTypes(t *testing.T) {
|
|
||||||
expected := `fn testExpressions() void {
|
|
||||||
const int_lit = 42;
|
|
||||||
const float_lit = 3.14;
|
|
||||||
const string_lit = "hello";
|
|
||||||
const char_lit = 'A';
|
|
||||||
const add = 1 + 2;
|
|
||||||
const sub = 5 - 3;
|
|
||||||
const mul = 4 * 5;
|
|
||||||
const div = 10 / 2;
|
|
||||||
const mod = 7 % 3;
|
|
||||||
const bit_and = 0xFF & 0x0F;
|
|
||||||
const bit_or = 0x10 | 0x01;
|
|
||||||
const bit_xor = 0xAA ^ 0x55;
|
|
||||||
const shift_left = 1 << 3;
|
|
||||||
const shift_right = 16 >> 2;
|
|
||||||
const eq = 5 == 5;
|
|
||||||
const ne = 3 != 4;
|
|
||||||
const lt = 2 < 5;
|
|
||||||
const gt = 8 > 3;
|
|
||||||
const le = 4 <= 4;
|
|
||||||
const ge = 6 >= 5;
|
|
||||||
const log_and = true and false;
|
|
||||||
const log_or = true or false;
|
|
||||||
const neg = -5;
|
|
||||||
const bit_not = ~0xFF;
|
|
||||||
const log_not = !true;
|
|
||||||
const addr_of = &int_lit;
|
|
||||||
const grouped = (2 + 3) * 4;
|
|
||||||
const field = obj.field;
|
|
||||||
const nested = obj.inner.value;
|
|
||||||
const elem = array[0];
|
|
||||||
const slice_elem = slice[i + 1];
|
|
||||||
const no_args = func();
|
|
||||||
const one_arg = print("text");
|
|
||||||
const multi_args = add(1, 2, 3);
|
|
||||||
const empty_init = .{};
|
|
||||||
const array_init = .{ 1, 2, 3 };
|
|
||||||
const struct_init = .{ .x = 10, .y = 20 };
|
|
||||||
const try_expr = try fallible();
|
|
||||||
const comptime_expr = comptime computeValue();
|
|
||||||
const nosuspend_expr = nosuspend asyncFunc();
|
|
||||||
const unreachable_expr = unreachable;
|
|
||||||
const async_expr = async startTask();
|
|
||||||
const await_expr = await future;
|
|
||||||
const resume_expr = resume frame;
|
|
||||||
const optional_unwrap = opt_value.?;
|
|
||||||
const optional_deref = ptr_value.*;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
root := &zig.Root{
|
|
||||||
ContainerMembers: []*zig.ContainerMember{
|
|
||||||
{
|
|
||||||
Decl: zig.DeclareFn(
|
|
||||||
"testExpressions",
|
|
||||||
zig.Id("void"),
|
|
||||||
zig.NewBlock(
|
|
||||||
// Literals
|
|
||||||
zig.DeclareVarStmt(true, []string{"int_lit"}, nil, zig.IntLit("42")),
|
|
||||||
zig.DeclareVarStmt(true, []string{"float_lit"}, nil, zig.FloatLit("3.14")),
|
|
||||||
zig.DeclareVarStmt(true, []string{"string_lit"}, nil, zig.StringLit("hello")),
|
|
||||||
zig.DeclareVarStmt(true, []string{"char_lit"}, nil, zig.CharLit("'A'")),
|
|
||||||
|
|
||||||
// Binary expressions - arithmetic
|
|
||||||
zig.DeclareVarStmt(true, []string{"add"}, nil, zig.Binary("+", zig.IntLit("1"), zig.IntLit("2"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"sub"}, nil, zig.Binary("-", zig.IntLit("5"), zig.IntLit("3"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"mul"}, nil, zig.Binary("*", zig.IntLit("4"), zig.IntLit("5"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"div"}, nil, zig.Binary("/", zig.IntLit("10"), zig.IntLit("2"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"mod"}, nil, zig.Binary("%", zig.IntLit("7"), zig.IntLit("3"))),
|
|
||||||
|
|
||||||
// Binary expressions - bitwise
|
|
||||||
zig.DeclareVarStmt(true, []string{"bit_and"}, nil, zig.Binary("&", zig.IntLit("0xFF"), zig.IntLit("0x0F"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"bit_or"}, nil, zig.Binary("|", zig.IntLit("0x10"), zig.IntLit("0x01"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"bit_xor"}, nil, zig.Binary("^", zig.IntLit("0xAA"), zig.IntLit("0x55"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"shift_left"}, nil, zig.Binary("<<", zig.IntLit("1"), zig.IntLit("3"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"shift_right"}, nil, zig.Binary(">>", zig.IntLit("16"), zig.IntLit("2"))),
|
|
||||||
|
|
||||||
// Binary expressions - comparison
|
|
||||||
zig.DeclareVarStmt(true, []string{"eq"}, nil, zig.Binary("==", zig.IntLit("5"), zig.IntLit("5"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"ne"}, nil, zig.Binary("!=", zig.IntLit("3"), zig.IntLit("4"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"lt"}, nil, zig.Binary("<", zig.IntLit("2"), zig.IntLit("5"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"gt"}, nil, zig.Binary(">", zig.IntLit("8"), zig.IntLit("3"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"le"}, nil, zig.Binary("<=", zig.IntLit("4"), zig.IntLit("4"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"ge"}, nil, zig.Binary(">=", zig.IntLit("6"), zig.IntLit("5"))),
|
|
||||||
|
|
||||||
// Binary expressions - logical
|
|
||||||
zig.DeclareVarStmt(true, []string{"log_and"}, nil, zig.Binary("and", zig.Id("true"), zig.Id("false"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"log_or"}, nil, zig.Binary("or", zig.Id("true"), zig.Id("false"))),
|
|
||||||
|
|
||||||
// Unary expressions
|
|
||||||
zig.DeclareVarStmt(true, []string{"neg"}, nil, zig.Unary("-", zig.IntLit("5"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"bit_not"}, nil, zig.Unary("~", zig.IntLit("0xFF"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"log_not"}, nil, zig.Unary("!", zig.Id("true"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"addr_of"}, nil, zig.Unary("&", zig.Id("int_lit"))),
|
|
||||||
|
|
||||||
// Grouped expression
|
|
||||||
zig.DeclareVarStmt(true, []string{"grouped"}, nil,
|
|
||||||
zig.Binary("*",
|
|
||||||
zig.Grouped(zig.Binary("+", zig.IntLit("2"), zig.IntLit("3"))),
|
|
||||||
zig.IntLit("4"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Field access
|
|
||||||
zig.DeclareVarStmt(true, []string{"field"}, nil, zig.FieldAccess(zig.Id("obj"), "field")),
|
|
||||||
zig.DeclareVarStmt(true, []string{"nested"}, nil,
|
|
||||||
zig.FieldAccess(zig.FieldAccess(zig.Id("obj"), "inner"), "value"),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Array/slice indexing
|
|
||||||
zig.DeclareVarStmt(true, []string{"elem"}, nil, zig.Index(zig.Id("array"), zig.IntLit("0"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"slice_elem"}, nil,
|
|
||||||
zig.Index(zig.Id("slice"), zig.Binary("+", zig.Id("i"), zig.IntLit("1"))),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Function calls
|
|
||||||
zig.DeclareVarStmt(true, []string{"no_args"}, nil, zig.Call(zig.Id("func"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"one_arg"}, nil, zig.Call(zig.Id("print"), zig.StringLit("text"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"multi_args"}, nil,
|
|
||||||
zig.Call(zig.Id("add"), zig.IntLit("1"), zig.IntLit("2"), zig.IntLit("3")),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Initializer lists
|
|
||||||
zig.DeclareVarStmt(true, []string{"empty_init"}, nil, zig.InitList()),
|
|
||||||
zig.DeclareVarStmt(true, []string{"array_init"}, nil, zig.InitList(zig.IntLit("1"), zig.IntLit("2"), zig.IntLit("3"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"struct_init"}, nil,
|
|
||||||
zig.InitListFields(
|
|
||||||
zig.FieldInitPair("x", zig.IntLit("10")),
|
|
||||||
zig.FieldInitPair("y", zig.IntLit("20")),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Special expressions
|
|
||||||
zig.DeclareVarStmt(true, []string{"try_expr"}, nil, zig.Try(zig.Call(zig.Id("fallible")))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"comptime_expr"}, nil, zig.Comptime(zig.Call(zig.Id("computeValue")))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"nosuspend_expr"}, nil, zig.Nosuspend(zig.Call(zig.Id("asyncFunc")))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"unreachable_expr"}, nil, zig.Unreachable()),
|
|
||||||
|
|
||||||
// Async expressions
|
|
||||||
zig.DeclareVarStmt(true, []string{"async_expr"}, nil, zig.Async(zig.Call(zig.Id("startTask")))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"await_expr"}, nil, zig.Await(zig.Id("future"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"resume_expr"}, nil, zig.Resume(zig.Id("frame"))),
|
|
||||||
|
|
||||||
// Optional handling
|
|
||||||
zig.DeclareVarStmt(true, []string{"optional_unwrap"}, nil, zig.DotQuestion(zig.Id("opt_value"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"optional_deref"}, nil, zig.DotAsterisk(zig.Id("ptr_value"))),
|
|
||||||
),
|
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
runZigASTTest(t, expected, root)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAllStatementTypes(t *testing.T) {
|
|
||||||
expected := `fn testStatements() void {
|
|
||||||
const x = 10;
|
|
||||||
var y: i32 = 20;
|
|
||||||
var z = x + y;
|
|
||||||
computeValue();
|
|
||||||
{
|
|
||||||
const inner = 5;
|
|
||||||
doWork();
|
|
||||||
}
|
|
||||||
if (x > 0) {
|
|
||||||
positive();
|
|
||||||
} else {
|
|
||||||
negative();
|
|
||||||
}
|
|
||||||
defer cleanup();
|
|
||||||
errdefer |err| handleError(err);
|
|
||||||
while (y > 0) : (y -= 1) {
|
|
||||||
processItem();
|
|
||||||
}
|
|
||||||
for (items, 0..) |item, idx| {
|
|
||||||
processIndexed(item, idx);
|
|
||||||
}
|
|
||||||
switch (x) {
|
|
||||||
0...9 => handleSmall(),
|
|
||||||
10 => return,
|
|
||||||
else => {
|
|
||||||
handleLarge();
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
loop: while (true) {
|
|
||||||
if (done()) break :loop;
|
|
||||||
continue :loop;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
root := &zig.Root{
|
|
||||||
ContainerMembers: []*zig.ContainerMember{
|
|
||||||
{
|
|
||||||
Decl: zig.DeclareFn(
|
|
||||||
"testStatements",
|
|
||||||
zig.Id("void"),
|
|
||||||
zig.NewBlock(
|
|
||||||
// Variable declarations
|
|
||||||
zig.DeclareVarStmt(true, []string{"x"}, nil, zig.IntLit("10")),
|
|
||||||
zig.DeclareVarStmt(false, []string{"y"}, zig.Id("i32"), zig.IntLit("20")),
|
|
||||||
zig.DeclareVarStmt(false, []string{"z"}, nil, zig.Binary("+", zig.Id("x"), zig.Id("y"))),
|
|
||||||
|
|
||||||
// Expression statement
|
|
||||||
zig.NewExprStmt(zig.Call(zig.Id("computeValue"))),
|
|
||||||
|
|
||||||
// Block statement
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.DeclareVarStmt(true, []string{"inner"}, nil, zig.IntLit("5")),
|
|
||||||
zig.NewExprStmt(zig.Call(zig.Id("doWork"))),
|
|
||||||
),
|
|
||||||
|
|
||||||
// If statement
|
|
||||||
zig.If(
|
|
||||||
zig.Binary(">", zig.Id("x"), zig.IntLit("0")),
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.NewExprStmt(zig.Call(zig.Id("positive"))),
|
|
||||||
),
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.NewExprStmt(zig.Call(zig.Id("negative"))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Defer statement
|
|
||||||
zig.Defer(zig.NewExprStmt(zig.Call(zig.Id("cleanup")))),
|
|
||||||
|
|
||||||
// Errdefer with payload
|
|
||||||
zig.ErrDefer(
|
|
||||||
zig.PayloadNames([]string{"err"}, []bool{false}),
|
|
||||||
zig.NewExprStmt(zig.Call(zig.Id("handleError"), zig.Id("err"))),
|
|
||||||
),
|
|
||||||
|
|
||||||
// While loop with continue expression
|
|
||||||
zig.WhileLoop(
|
|
||||||
zig.Binary(">", zig.Id("y"), zig.IntLit("0")),
|
|
||||||
zig.Binary("-=", zig.Id("y"), zig.IntLit("1")),
|
|
||||||
nil,
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.NewExprStmt(zig.Call(zig.Id("processItem"))),
|
|
||||||
),
|
|
||||||
nil,
|
|
||||||
),
|
|
||||||
|
|
||||||
// For loop with payload
|
|
||||||
zig.ForLoop(
|
|
||||||
[]zig.ForArg{
|
|
||||||
zig.ForArgExpr(zig.Id("items")),
|
|
||||||
{Expr: zig.IntLit("0"), From: zig.IntLit("")},
|
|
||||||
},
|
|
||||||
zig.PayloadNames([]string{"item", "idx"}, []bool{false, false}),
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.NewExprStmt(zig.Call(zig.Id("processIndexed"), zig.Id("item"), zig.Id("idx"))),
|
|
||||||
),
|
|
||||||
nil,
|
|
||||||
),
|
|
||||||
|
|
||||||
// Switch statement
|
|
||||||
zig.Switch(
|
|
||||||
zig.Id("x"),
|
|
||||||
zig.Prong(
|
|
||||||
[]*zig.SwitchCase{
|
|
||||||
zig.Case(zig.IntLit("0"), zig.IntLit("9")),
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
zig.NewExprStmt(zig.Call(zig.Id("handleSmall"))),
|
|
||||||
),
|
|
||||||
zig.Prong(
|
|
||||||
[]*zig.SwitchCase{
|
|
||||||
zig.Case(zig.IntLit("10"), nil),
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
zig.Return(nil),
|
|
||||||
),
|
|
||||||
zig.Prong(
|
|
||||||
[]*zig.SwitchCase{zig.ElseCase()},
|
|
||||||
nil,
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.NewExprStmt(zig.Call(zig.Id("handleLarge"))),
|
|
||||||
zig.Break("", nil),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Labeled while loop with break and continue
|
|
||||||
&zig.LoopStmt{
|
|
||||||
Kind: zig.LoopWhile,
|
|
||||||
Prefix: &zig.WhilePrefix{
|
|
||||||
Cond: zig.Id("true"),
|
|
||||||
},
|
|
||||||
Body: &zig.BlockStmt{
|
|
||||||
Block: &zig.Block{
|
|
||||||
Label: "loop",
|
|
||||||
Stmts: []zig.Stmt{
|
|
||||||
zig.If(
|
|
||||||
zig.Call(zig.Id("done")),
|
|
||||||
zig.Break("loop", nil),
|
|
||||||
nil,
|
|
||||||
),
|
|
||||||
zig.Continue("loop"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Return statement
|
|
||||||
zig.Return(nil),
|
|
||||||
),
|
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
runZigASTTest(t, expected, root)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAllTypeExpressions(t *testing.T) {
|
|
||||||
expected := `fn testTypes() void {
|
|
||||||
const a: i32 = 42;
|
|
||||||
const b: u8 = 255;
|
|
||||||
const c: f64 = 3.14;
|
|
||||||
const d: bool = true;
|
|
||||||
const e: void = .{};
|
|
||||||
const ptr: *i32 = &a;
|
|
||||||
const const_ptr: *const i32 = &a;
|
|
||||||
const multi_ptr: [*]i32 = &array;
|
|
||||||
const slice: []i32 = getSlice(array);
|
|
||||||
const array_type: [5]i32 = .{ 1, 2, 3, 4, 5 };
|
|
||||||
const opt: ?i32 = 42;
|
|
||||||
const err_union: MyError!i32 = 42;
|
|
||||||
const any: anytype = 42;
|
|
||||||
const struct_type: MyStruct = .{};
|
|
||||||
const enum_type: MyEnum = .value;
|
|
||||||
const union_type: MyUnion = .{ .field = 10 };
|
|
||||||
}
|
|
||||||
|
|
||||||
const MyError = error{
|
|
||||||
OutOfMemory,
|
|
||||||
InvalidInput,
|
|
||||||
};
|
|
||||||
|
|
||||||
const MyStruct = struct {
|
|
||||||
x: i32,
|
|
||||||
y: f64,
|
|
||||||
};
|
|
||||||
|
|
||||||
const MyEnum = enum {
|
|
||||||
value,
|
|
||||||
other,
|
|
||||||
};
|
|
||||||
|
|
||||||
const MyUnion = union(enum) {
|
|
||||||
field: i32,
|
|
||||||
other: f64,
|
|
||||||
};
|
|
||||||
`
|
|
||||||
|
|
||||||
root := &zig.Root{
|
|
||||||
ContainerMembers: []*zig.ContainerMember{
|
|
||||||
{
|
|
||||||
Decl: zig.DeclareFn(
|
|
||||||
"testTypes",
|
|
||||||
zig.Id("void"),
|
|
||||||
zig.NewBlock(
|
|
||||||
// Basic types
|
|
||||||
zig.DeclareVarStmt(true, []string{"a"}, zig.Id("i32"), zig.IntLit("42")),
|
|
||||||
zig.DeclareVarStmt(true, []string{"b"}, zig.Id("u8"), zig.IntLit("255")),
|
|
||||||
zig.DeclareVarStmt(true, []string{"c"}, zig.Id("f64"), zig.FloatLit("3.14")),
|
|
||||||
zig.DeclareVarStmt(true, []string{"d"}, zig.Id("bool"), zig.Id("true")),
|
|
||||||
zig.DeclareVarStmt(true, []string{"e"}, zig.Id("void"), zig.InitList()),
|
|
||||||
|
|
||||||
// Pointer types
|
|
||||||
zig.DeclareVarStmt(true, []string{"ptr"}, zig.PointerType(zig.Id("i32")), zig.Unary("&", zig.Id("a"))),
|
|
||||||
zig.DeclareVarStmt(true, []string{"const_ptr"},
|
|
||||||
&zig.PrefixTypeExpr{Op: "*const", Base: zig.Id("i32")},
|
|
||||||
zig.Unary("&", zig.Id("a")),
|
|
||||||
),
|
|
||||||
zig.DeclareVarStmt(true, []string{"multi_ptr"},
|
|
||||||
&zig.PrefixTypeExpr{Op: "[*]", Base: zig.Id("i32")},
|
|
||||||
zig.Unary("&", zig.Id("array")),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Slice and array types
|
|
||||||
zig.DeclareVarStmt(true, []string{"slice"}, zig.SliceType(zig.Id("i32")),
|
|
||||||
// TODO: array[0..] syntax not properly supported yet
|
|
||||||
zig.Call(zig.Id("getSlice"), zig.Id("array")),
|
|
||||||
),
|
|
||||||
zig.DeclareVarStmt(true, []string{"array_type"}, zig.ArrayType(zig.IntLit("5"), zig.Id("i32")),
|
|
||||||
zig.InitList(zig.IntLit("1"), zig.IntLit("2"), zig.IntLit("3"), zig.IntLit("4"), zig.IntLit("5")),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Optional and error union types
|
|
||||||
zig.DeclareVarStmt(true, []string{"opt"}, zig.OptionalType(zig.Id("i32")), zig.IntLit("42")),
|
|
||||||
zig.DeclareVarStmt(true, []string{"err_union"}, zig.ErrorUnionType(zig.Id("MyError"), zig.Id("i32")), zig.IntLit("42")),
|
|
||||||
|
|
||||||
// Special types
|
|
||||||
zig.DeclareVarStmt(true, []string{"any"}, zig.Id("anytype"), zig.IntLit("42")),
|
|
||||||
zig.DeclareVarStmt(true, []string{"struct_type"}, zig.Id("MyStruct"), zig.InitList()),
|
|
||||||
zig.DeclareVarStmt(true, []string{"enum_type"}, zig.Id("MyEnum"), zig.FieldAccess(&zig.Identifier{Name: ""}, "value")),
|
|
||||||
zig.DeclareVarStmt(true, []string{"union_type"}, zig.Id("MyUnion"),
|
|
||||||
zig.InitListFields(zig.FieldInitPair("field", zig.IntLit("10"))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Decl: zig.DeclareGlobalVar("MyError",
|
|
||||||
zig.ErrorSet("OutOfMemory", "InvalidInput"),
|
|
||||||
zig.GlobalVarConst,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Decl: zig.DeclareGlobalVar("MyStruct",
|
|
||||||
zig.StructDecl(
|
|
||||||
zig.Field("x", zig.Id("i32"), nil, nil),
|
|
||||||
zig.Field("y", zig.Id("f64"), nil, nil),
|
|
||||||
),
|
|
||||||
zig.GlobalVarConst,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Decl: zig.DeclareGlobalVar("MyEnum",
|
|
||||||
zig.EnumDecl(nil,
|
|
||||||
zig.Field("value", nil, nil, nil),
|
|
||||||
zig.Field("other", nil, nil, nil),
|
|
||||||
),
|
|
||||||
zig.GlobalVarConst,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Decl: zig.DeclareGlobalVar("MyUnion",
|
|
||||||
&zig.ContainerDecl{
|
|
||||||
Kind: zig.ContainerUnion,
|
|
||||||
TagType: zig.Id("enum"),
|
|
||||||
Fields: []*zig.ContainerMember{
|
|
||||||
zig.Field("field", zig.Id("i32"), nil, nil),
|
|
||||||
zig.Field("other", zig.Id("f64"), nil, nil),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
zig.GlobalVarConst,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
runZigASTTest(t, expected, root)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestControlFlowPatterns(t *testing.T) {
|
|
||||||
expected := `fn testControlFlow() !void {
|
|
||||||
var items = .{ 1, 2, null, 4 };
|
|
||||||
var sum: i32 = 0;
|
|
||||||
outer: for (items, 0..) |maybe_item, i| {
|
|
||||||
const item = maybe_item orelse {
|
|
||||||
if (i == 0) return error.InvalidInput;
|
|
||||||
continue :outer;
|
|
||||||
};
|
|
||||||
inner: while (item > 0) {
|
|
||||||
defer sum += 1;
|
|
||||||
switch (item) {
|
|
||||||
1 => break :inner,
|
|
||||||
2...10 => |val| {
|
|
||||||
if (val == 5) break :outer;
|
|
||||||
continue :inner;
|
|
||||||
},
|
|
||||||
else => return error.OutOfRange,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (item < 0) {
|
|
||||||
errdefer |err| std.log.err("Error: {}", .{err});
|
|
||||||
try processNegative(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const result = blk: {
|
|
||||||
if (sum > 100) break :blk sum;
|
|
||||||
break :blk 0;
|
|
||||||
};
|
|
||||||
return if (result > 0) .{} else error.NoResult;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
root := &zig.Root{
|
|
||||||
ContainerMembers: []*zig.ContainerMember{
|
|
||||||
{
|
|
||||||
Decl: zig.DeclareFn(
|
|
||||||
"testControlFlow",
|
|
||||||
zig.ErrorUnionType(nil, zig.Id("void")),
|
|
||||||
zig.NewBlock(
|
|
||||||
// var items = [_]?i32{ 1, 2, null, 4 };
|
|
||||||
zig.DeclareVarStmt(false, []string{"items"}, nil,
|
|
||||||
&zig.InitListExpr{
|
|
||||||
Values: []zig.Expr{
|
|
||||||
zig.IntLit("1"),
|
|
||||||
zig.IntLit("2"),
|
|
||||||
zig.Id("null"),
|
|
||||||
zig.IntLit("4"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// var sum: i32 = 0;
|
|
||||||
zig.DeclareVarStmt(false, []string{"sum"}, zig.Id("i32"), zig.IntLit("0")),
|
|
||||||
|
|
||||||
// outer: for loop with orelse handling
|
|
||||||
&zig.LoopStmt{
|
|
||||||
Kind: zig.LoopFor,
|
|
||||||
Prefix: &zig.ForPrefix{
|
|
||||||
Args: []zig.ForArg{
|
|
||||||
zig.ForArgExpr(zig.Id("items")),
|
|
||||||
{Expr: zig.IntLit("0"), From: zig.IntLit("")},
|
|
||||||
},
|
|
||||||
Payload: zig.PayloadNames([]string{"maybe_item", "i"}, []bool{false, false}),
|
|
||||||
},
|
|
||||||
Body: &zig.BlockStmt{
|
|
||||||
Block: &zig.Block{
|
|
||||||
Label: "outer",
|
|
||||||
Stmts: []zig.Stmt{
|
|
||||||
// const item = maybe_item orelse { ... };
|
|
||||||
zig.DeclareVarStmt(true, []string{"item"}, nil,
|
|
||||||
zig.Binary("orelse", zig.Id("maybe_item"),
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.If(
|
|
||||||
zig.Binary("==", zig.Id("i"), zig.IntLit("0")),
|
|
||||||
zig.Return(zig.FieldAccess(&zig.Identifier{Name: "error"}, "InvalidInput")),
|
|
||||||
nil,
|
|
||||||
),
|
|
||||||
zig.Continue("outer"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// inner: while loop with defer
|
|
||||||
&zig.LoopStmt{
|
|
||||||
Kind: zig.LoopWhile,
|
|
||||||
Prefix: &zig.WhilePrefix{
|
|
||||||
Cond: zig.Binary(">", zig.Id("item"), zig.IntLit("0")),
|
|
||||||
},
|
|
||||||
Body: &zig.BlockStmt{
|
|
||||||
Block: &zig.Block{
|
|
||||||
Label: "inner",
|
|
||||||
Stmts: []zig.Stmt{
|
|
||||||
// defer sum += 1;
|
|
||||||
zig.Defer(zig.NewExprStmt(zig.Binary("+=", zig.Id("sum"), zig.IntLit("1")))),
|
|
||||||
|
|
||||||
// switch with labeled breaks
|
|
||||||
zig.Switch(
|
|
||||||
zig.Id("item"),
|
|
||||||
zig.Prong(
|
|
||||||
[]*zig.SwitchCase{zig.Case(zig.IntLit("1"), nil)},
|
|
||||||
nil,
|
|
||||||
zig.Break("inner", nil),
|
|
||||||
),
|
|
||||||
zig.Prong(
|
|
||||||
[]*zig.SwitchCase{zig.Case(zig.IntLit("2"), zig.IntLit("10"))},
|
|
||||||
zig.PayloadNames([]string{"val"}, []bool{false}),
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.If(
|
|
||||||
zig.Binary("==", zig.Id("val"), zig.IntLit("5")),
|
|
||||||
zig.Break("outer", nil),
|
|
||||||
nil,
|
|
||||||
),
|
|
||||||
zig.Continue("inner"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
zig.Prong(
|
|
||||||
[]*zig.SwitchCase{zig.ElseCase()},
|
|
||||||
nil,
|
|
||||||
zig.Return(zig.FieldAccess(&zig.Identifier{Name: "error"}, "OutOfRange")),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// if with errdefer
|
|
||||||
zig.If(
|
|
||||||
zig.Binary("<", zig.Id("item"), zig.IntLit("0")),
|
|
||||||
zig.NewBlockStmt(
|
|
||||||
zig.ErrDefer(
|
|
||||||
zig.PayloadNames([]string{"err"}, []bool{false}),
|
|
||||||
zig.NewExprStmt(
|
|
||||||
zig.Call(
|
|
||||||
zig.FieldAccess(zig.FieldAccess(zig.Id("std"), "log"), "err"),
|
|
||||||
zig.StringLit("Error: {}"),
|
|
||||||
zig.InitList(zig.Id("err")),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
zig.NewExprStmt(zig.Try(zig.Call(zig.Id("processNegative"), zig.Id("item")))),
|
|
||||||
),
|
|
||||||
nil,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// const result = blk: { ... };
|
|
||||||
zig.DeclareVarStmt(true, []string{"result"}, nil,
|
|
||||||
&zig.LabeledBlock{
|
|
||||||
Label: "blk",
|
|
||||||
Block: zig.NewBlock(
|
|
||||||
zig.If(
|
|
||||||
zig.Binary(">", zig.Id("sum"), zig.IntLit("100")),
|
|
||||||
zig.Break("blk", zig.Id("sum")),
|
|
||||||
nil,
|
|
||||||
),
|
|
||||||
zig.Break("blk", zig.IntLit("0")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
|
|
||||||
// return if (result > 0) {} else error.NoResult;
|
|
||||||
zig.Return(
|
|
||||||
zig.IfExpression(
|
|
||||||
zig.Binary(">", zig.Id("result"), zig.IntLit("0")),
|
|
||||||
zig.InitList(),
|
|
||||||
zig.FieldAccess(&zig.Identifier{Name: "error"}, "NoResult"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
runZigASTTest(t, expected, root)
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
print("Hello, world!\n")
|
print("Hello, world\n")
|
||||||
}
|
}
|
@ -1 +0,0 @@
|
|||||||
Hello, world!
|
|
@ -1 +0,0 @@
|
|||||||
First Second
|
|
@ -1,8 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
print("First")
|
|
||||||
print(" ")
|
|
||||||
print("Second")
|
|
||||||
print("\n")
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
Hello World!
|
|
||||||
Line 1
|
|
||||||
Line 2
|
|
||||||
Quote: "test"
|
|
||||||
Backslash: \
|
|
@ -1,8 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
print("Hello\tWorld!\n")
|
|
||||||
print("Line 1\nLine 2\n")
|
|
||||||
print("Quote: \"test\"\n")
|
|
||||||
print("Backslash: \\\n")
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user