Generate hello world Zig code
This commit is contained in:
@@ -106,6 +106,26 @@ type Stmt interface {
|
|||||||
isStmt()
|
isStmt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExprStmt represents an expression statement (e.g. a function call as a statement).
|
||||||
|
type ExprStmt struct {
|
||||||
|
Expr Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ExprStmt) isStmt() {}
|
||||||
|
|
||||||
|
// VarDeclStmt represents a variable or const declaration at statement level, supporting destructuring and multi-var declarations.
|
||||||
|
type VarDeclStmt struct {
|
||||||
|
Const bool
|
||||||
|
Pattern VarPattern // Destructuring or multiple variable names
|
||||||
|
Type TypeExpr // Optional
|
||||||
|
Value Expr // Optional initializer
|
||||||
|
ByteAlign Expr // Optional
|
||||||
|
AddrSpace Expr // Optional
|
||||||
|
LinkSection Expr // Optional
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*VarDeclStmt) isStmt() {}
|
||||||
|
|
||||||
// ReturnStmt represents a 'return' statement.
|
// ReturnStmt represents a 'return' statement.
|
||||||
type ReturnStmt struct {
|
type ReturnStmt struct {
|
||||||
Value Expr // Optional
|
Value Expr // Optional
|
||||||
@@ -321,6 +341,9 @@ type CallExpr struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FieldAccessExpr represents a field/member access as a suffix operation (e.g. foo.bar).
|
// FieldAccessExpr represents a field/member access as a suffix operation (e.g. foo.bar).
|
||||||
|
//
|
||||||
|
// Note: in order to call a function on an object, use a CallExpr with a Fun of a FieldAccessExpr.
|
||||||
|
// See TestHelloWorld for example.
|
||||||
type FieldAccessExpr struct {
|
type FieldAccessExpr struct {
|
||||||
Receiver Expr // The object being accessed
|
Receiver Expr // The object being accessed
|
||||||
Field string // The field name
|
Field string // The field name
|
||||||
@@ -475,23 +498,3 @@ type SwitchElseProng struct {
|
|||||||
type VarPattern struct {
|
type VarPattern struct {
|
||||||
Names []string // Variable names (single or multiple for destructuring)
|
Names []string // Variable names (single or multiple for destructuring)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VarDeclStmt represents a variable or const declaration at statement level, supporting destructuring and multi-var declarations.
|
|
||||||
type VarDeclStmt struct {
|
|
||||||
Const bool
|
|
||||||
Pattern VarPattern // Destructuring or multiple variable names
|
|
||||||
Type TypeExpr // Optional
|
|
||||||
Value Expr // Optional initializer
|
|
||||||
ByteAlign Expr // Optional
|
|
||||||
AddrSpace Expr // Optional
|
|
||||||
LinkSection Expr // Optional
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*VarDeclStmt) isStmt() {}
|
|
||||||
|
|
||||||
// DotCallExpr represents a method call expression where a method is called on a receiver.
|
|
||||||
// For example, in the expression `foo.bar()`, `foo` is the Receiver and `bar()` is the Call.
|
|
||||||
type DotCallExpr struct {
|
|
||||||
Receiver Expr // The expression being called (e.g. foo)
|
|
||||||
Call *CallExpr
|
|
||||||
}
|
|
||||||
|
@@ -12,15 +12,18 @@ type formatter struct {
|
|||||||
indent int // indentation level
|
indent int // indentation level
|
||||||
}
|
}
|
||||||
|
|
||||||
const indentStr = " " // 4 spaces per indent
|
// indentStr defines the string used for each indentation level (4 spaces).
|
||||||
|
const indentStr = " "
|
||||||
|
|
||||||
|
// Writef writes formatted text to the underlying writer and updates line/col counters.
|
||||||
|
// 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' {
|
||||||
f.line++
|
f.line++
|
||||||
f.col = 1
|
f.col = 1
|
||||||
// If next character is not a newline and not end, and not a closing brace, write indentation
|
// After a newline, write indentation for the next line unless it's a closing brace or another newline.
|
||||||
if i+1 < len(s) && s[i+1] != '\n' && s[i+1] != '}' {
|
if i+1 < len(s) && s[i+1] != '\n' && s[i+1] != '}' {
|
||||||
f.writeIndent()
|
f.writeIndent()
|
||||||
}
|
}
|
||||||
@@ -37,6 +40,8 @@ func (f *formatter) Writef(format string, a ...any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeIndent writes the current indentation level to the output.
|
||||||
|
// Call this at the start of a new line before writing statements or closing braces.
|
||||||
func (f *formatter) writeIndent() {
|
func (f *formatter) writeIndent() {
|
||||||
for i := 0; i < f.indent; i++ {
|
for i := 0; i < f.indent; i++ {
|
||||||
if _, err := f.w.Write([]byte(indentStr)); err != nil {
|
if _, err := f.w.Write([]byte(indentStr)); err != nil {
|
||||||
@@ -46,6 +51,7 @@ func (f *formatter) writeIndent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write is the entry point for formatting a Zig AST.
|
||||||
func Write(w io.Writer, root *Root) (err error) {
|
func Write(w io.Writer, root *Root) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -70,17 +76,27 @@ func Write(w io.Writer, root *Root) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeDecl emits a top-level declaration.
|
||||||
func writeDecl(f *formatter, decl Decl) {
|
func writeDecl(f *formatter, decl Decl) {
|
||||||
switch fn := decl.(type) {
|
switch d := decl.(type) {
|
||||||
case *FnDecl:
|
case *FnDecl:
|
||||||
f.Writef("fn %s(", fn.Name)
|
f.Writef("\nfn %s(", d.Name)
|
||||||
writeParams(f, fn.Params)
|
writeParams(f, d.Params)
|
||||||
f.Writef(") ")
|
f.Writef(") ")
|
||||||
writeTypeExpr(f, fn.ReturnType)
|
writeTypeExpr(f, d.ReturnType)
|
||||||
writeBlock(f, fn.Body)
|
writeBlock(f, d.Body)
|
||||||
|
case *GlobalVarDecl:
|
||||||
|
if d.Const {
|
||||||
|
f.Writef("const %s = ", d.Name)
|
||||||
|
} else {
|
||||||
|
f.Writef("var %s = ", d.Name)
|
||||||
|
}
|
||||||
|
writeExpr(f, d.Value)
|
||||||
|
f.Writef(";\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeParams emits function parameters, separated by commas.
|
||||||
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 {
|
||||||
@@ -93,6 +109,7 @@ func writeParams(f *formatter, params []*ParamDecl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeTypeExpr emits a type expression.
|
||||||
func writeTypeExpr(f *formatter, typ TypeExpr) {
|
func writeTypeExpr(f *formatter, typ TypeExpr) {
|
||||||
switch t := typ.(type) {
|
switch t := typ.(type) {
|
||||||
case *Identifier:
|
case *Identifier:
|
||||||
@@ -104,29 +121,75 @@ func writeTypeExpr(f *formatter, typ TypeExpr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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++
|
f.indent++ // Increase indentation for block contents.
|
||||||
for i, stmt := range block.Stmts {
|
for i, stmt := range block.Stmts {
|
||||||
f.writeIndent()
|
f.writeIndent() // Indent each statement.
|
||||||
writeStmt(f, stmt)
|
writeStmt(f, stmt)
|
||||||
if i < len(block.Stmts)-1 {
|
if i < len(block.Stmts)-1 {
|
||||||
f.Writef("\n")
|
f.Writef("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.indent--
|
f.indent-- // Decrease indentation before closing brace.
|
||||||
f.Writef("\n")
|
f.Writef("\n")
|
||||||
f.writeIndent()
|
f.writeIndent() // Indent the closing brace.
|
||||||
f.Writef("}\n")
|
f.Writef("}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeStmt emits a statement. Indentation is handled by the caller (writeBlock).
|
||||||
func writeStmt(f *formatter, stmt Stmt) {
|
func writeStmt(f *formatter, stmt Stmt) {
|
||||||
switch stmt.(type) {
|
switch s := stmt.(type) {
|
||||||
case *ReturnStmt:
|
case *ReturnStmt:
|
||||||
f.Writef("return;")
|
f.Writef("return")
|
||||||
|
if s.Value != nil {
|
||||||
|
f.Writef(" ")
|
||||||
|
writeExpr(f, s.Value)
|
||||||
|
}
|
||||||
|
f.Writef(";")
|
||||||
|
case *ExprStmt:
|
||||||
|
writeExpr(f, s.Expr)
|
||||||
|
f.Writef(";")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeExpr emits an expression.
|
||||||
|
func writeExpr(f *formatter, expr Expr) {
|
||||||
|
switch e := expr.(type) {
|
||||||
|
case *Identifier:
|
||||||
|
f.Writef("%s", e.Name)
|
||||||
|
case *CallExpr:
|
||||||
|
writeExpr(f, e.Fun)
|
||||||
|
f.Writef("(")
|
||||||
|
for i, arg := range e.Args {
|
||||||
|
if i > 0 {
|
||||||
|
f.Writef(", ")
|
||||||
|
}
|
||||||
|
writeExpr(f, arg)
|
||||||
|
}
|
||||||
|
f.Writef(")")
|
||||||
|
case *FieldAccessExpr:
|
||||||
|
writeExpr(f, e.Receiver)
|
||||||
|
f.Writef(".%s", e.Field)
|
||||||
|
case *Literal:
|
||||||
|
switch e.Kind {
|
||||||
|
case "string":
|
||||||
|
f.Writef("%q", e.Value)
|
||||||
|
default:
|
||||||
|
f.Writef("%v", e.Value)
|
||||||
|
}
|
||||||
|
case *InitListExpr:
|
||||||
|
if e.Empty {
|
||||||
|
f.Writef(".{}")
|
||||||
|
} else {
|
||||||
|
f.Writef(".{")
|
||||||
|
// TODO
|
||||||
|
f.Writef("}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,21 +19,49 @@ func Expect[T cmp.Ordered](expected, actual T) error {
|
|||||||
func TestHelloWorld(t *testing.T) {
|
func TestHelloWorld(t *testing.T) {
|
||||||
expected := `//! Hello, world!
|
expected := `//! Hello, world!
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
fn main() void {
|
fn main() void {
|
||||||
return;
|
std.debug.print("Hello, world!\n", .{});
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
root := &zig.Root{
|
root := &zig.Root{
|
||||||
ContainerDocComment: "Hello, world!",
|
ContainerDocComment: "Hello, world!",
|
||||||
ContainerMembers: []*zig.ContainerMember{
|
ContainerMembers: []*zig.ContainerMember{
|
||||||
|
{
|
||||||
|
Decl: &zig.GlobalVarDecl{
|
||||||
|
Const: true,
|
||||||
|
Name: "std",
|
||||||
|
Value: &zig.CallExpr{
|
||||||
|
Fun: &zig.Identifier{Name: "@import"},
|
||||||
|
Args: []zig.Expr{
|
||||||
|
&zig.Literal{Kind: "string", Value: "std"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Decl: &zig.FnDecl{
|
Decl: &zig.FnDecl{
|
||||||
Name: "main",
|
Name: "main",
|
||||||
ReturnType: &zig.Identifier{Name: "void"},
|
ReturnType: &zig.Identifier{Name: "void"},
|
||||||
Body: &zig.Block{
|
Body: &zig.Block{
|
||||||
Stmts: []zig.Stmt{
|
Stmts: []zig.Stmt{
|
||||||
&zig.ReturnStmt{},
|
&zig.ExprStmt{
|
||||||
|
Expr: &zig.CallExpr{
|
||||||
|
Fun: &zig.FieldAccessExpr{
|
||||||
|
Receiver: &zig.FieldAccessExpr{
|
||||||
|
Receiver: &zig.Identifier{Name: "std"},
|
||||||
|
Field: "debug",
|
||||||
|
},
|
||||||
|
Field: "print",
|
||||||
|
},
|
||||||
|
Args: []zig.Expr{
|
||||||
|
&zig.Literal{Kind: "string", Value: "Hello, world!\n"},
|
||||||
|
&zig.InitListExpr{Empty: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user