Starting Zig code generator with one test
This commit is contained in:
parent
bdf7ea85d3
commit
0bf8c3ef6b
2
Makefile
2
Makefile
@ -1,2 +1,2 @@
|
||||
run:
|
||||
go run internal/main.go -o hello.zig programs/hello.go
|
||||
go run internal/main.go -o hello.zig programs/hello.go && zig run hello.zig
|
50
internal/zig/ast.go
Normal file
50
internal/zig/ast.go
Normal file
@ -0,0 +1,50 @@
|
||||
package zig
|
||||
|
||||
// https://github.com/ziglang/zig-spec/blob/master/grammar/grammar.peg
|
||||
|
||||
type Root struct {
|
||||
ContainerDocComment string // //! Doc Comment
|
||||
ContainerMembers []*ContainerMember
|
||||
}
|
||||
|
||||
type ContainerMember struct {
|
||||
// FIXME
|
||||
Decls []Decl
|
||||
}
|
||||
|
||||
type Decl interface{}
|
||||
|
||||
type FnDecl struct {
|
||||
Name string
|
||||
Params []*ParamDecl
|
||||
CallConv string
|
||||
ReturnType TypeExpr
|
||||
Body *Block // nil means semicolon
|
||||
}
|
||||
|
||||
type ParamDecl struct {
|
||||
DocComment string // ??? It's what it says
|
||||
Name string // Can be empty
|
||||
Type TypeExpr // anytype when empty
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
Label string
|
||||
Stmts []Stmt
|
||||
}
|
||||
|
||||
type Stmt interface{}
|
||||
|
||||
type ReturnStmt struct{}
|
||||
|
||||
type Expr interface{}
|
||||
|
||||
// This will need to become a real type expr someday
|
||||
type TypeExpr string
|
||||
|
||||
func (t TypeExpr) String() string {
|
||||
if string(t) == "" {
|
||||
return "anytype"
|
||||
}
|
||||
return string(t)
|
||||
}
|
108
internal/zig/zig.go
Normal file
108
internal/zig/zig.go
Normal file
@ -0,0 +1,108 @@
|
||||
package zig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type formatter struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (f *formatter) WriteString(s string) (n int, err error) {
|
||||
return f.w.Write([]byte(s))
|
||||
}
|
||||
|
||||
func (f *formatter) Writef(format string, a ...any) (err error) {
|
||||
_, err = f.w.Write(fmt.Appendf(nil, format, a...))
|
||||
return err
|
||||
}
|
||||
|
||||
func Write(w io.Writer, root *Root) error {
|
||||
f := &formatter{
|
||||
w: w,
|
||||
}
|
||||
|
||||
if root.ContainerDocComment != "" {
|
||||
err := f.Writef("//! %s\n\n", root.ContainerDocComment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, member := range root.ContainerMembers {
|
||||
for _, decl := range member.Decls {
|
||||
if err := writeDecl(f, decl); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeDecl(f *formatter, decl Decl) (err error) {
|
||||
switch typ := decl.(type) {
|
||||
case *FnDecl:
|
||||
if err = f.Writef("fn %s(", typ.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = writeParams(f, typ.Params); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = f.Writef(") %s", typ.ReturnType); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = writeBlock(f, typ.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeParams(f *formatter, params []*ParamDecl) (err error) {
|
||||
for _, param := range params {
|
||||
if param.Name != "" {
|
||||
if err = f.Writef("%s: ", param.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = f.Writef("%s", param.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeBlock(f *formatter, block *Block) (err error) {
|
||||
if block == nil {
|
||||
if _, err = f.WriteString(";"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err = f.Writef(" {\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, stmt := range block.Stmts {
|
||||
if err = writeStmt(f, stmt); err != nil {
|
||||
return err
|
||||
}
|
||||
// Should this be the job of the formatter?
|
||||
if _, err = f.WriteString("\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = f.Writef("}\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeStmt(f *formatter, stmt Stmt) (err error) {
|
||||
switch stmt.(type) {
|
||||
case *ReturnStmt:
|
||||
if _, err = f.WriteString("return;"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
55
internal/zig/zig_test.go
Normal file
55
internal/zig/zig_test.go
Normal file
@ -0,0 +1,55 @@
|
||||
package zig_test
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.frop.prof/luke/go-zig-compiler/internal/zig"
|
||||
)
|
||||
|
||||
func Expect[T cmp.Ordered](expected, actual T) error {
|
||||
if expected != actual {
|
||||
return fmt.Errorf("\nExpected: %v\nActual: %v", expected, actual)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestHelloWorld(t *testing.T) {
|
||||
expected := `//! Hello, world!
|
||||
|
||||
fn main() void {
|
||||
return;
|
||||
}
|
||||
`
|
||||
|
||||
root := &zig.Root{
|
||||
ContainerDocComment: "Hello, world!",
|
||||
ContainerMembers: []*zig.ContainerMember{
|
||||
{
|
||||
Decls: []zig.Decl{
|
||||
&zig.FnDecl{
|
||||
Name: "main",
|
||||
ReturnType: "void",
|
||||
Body: &zig.Block{
|
||||
Stmts: []zig.Stmt{
|
||||
&zig.ReturnStmt{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
sb := new(strings.Builder)
|
||||
err := zig.Write(sb, root)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
actual := sb.String()
|
||||
|
||||
if err = Expect(expected, actual); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user