Refactored code generator, tracks indentation, no error checking
This commit is contained in:
parent
dba4617520
commit
d002309e93
@ -6,123 +6,127 @@ import (
|
||||
)
|
||||
|
||||
type formatter struct {
|
||||
io.Writer
|
||||
w io.Writer
|
||||
line int // 1-based
|
||||
col int // 1-based, reset to 1 after newline
|
||||
indent int // indentation level
|
||||
}
|
||||
|
||||
func (f *formatter) WriteString(s string) (n int, err error) {
|
||||
return f.Write([]byte(s))
|
||||
const indentStr = " " // 4 spaces per indent
|
||||
|
||||
func (f *formatter) Writef(format string, a ...any) {
|
||||
s := fmt.Sprintf(format, a...)
|
||||
for i, r := range s {
|
||||
if r == '\n' {
|
||||
f.line++
|
||||
f.col = 1
|
||||
// If next character is not a newline and not end, and not a closing brace, write indentation
|
||||
if i+1 < len(s) && s[i+1] != '\n' && s[i+1] != '}' {
|
||||
f.writeIndent()
|
||||
}
|
||||
} else {
|
||||
if f.col == 0 {
|
||||
f.col = 1
|
||||
} else {
|
||||
f.col++
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, err := f.w.Write([]byte(s)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *formatter) Writef(format string, a ...any) (err error) {
|
||||
_, err = fmt.Fprintf(f, format, a...)
|
||||
return err
|
||||
func (f *formatter) writeIndent() {
|
||||
for i := 0; i < f.indent; i++ {
|
||||
if _, err := f.w.Write([]byte(indentStr)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
f.col += len(indentStr)
|
||||
}
|
||||
}
|
||||
|
||||
func Write(w io.Writer, root *Root) error {
|
||||
f := &formatter{Writer: w}
|
||||
func Write(w io.Writer, root *Root) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if e, ok := r.(error); ok {
|
||||
err = e
|
||||
} else {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
f := &formatter{w: w, line: 1, col: 1, indent: 0}
|
||||
|
||||
if root.ContainerDocComment != "" {
|
||||
if err := f.Writef("//! %s\n\n", root.ContainerDocComment); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Writef("//! %s\n\n", root.ContainerDocComment)
|
||||
}
|
||||
for _, member := range root.ContainerMembers {
|
||||
// Only handle Decl for now (fields not needed for hello world)
|
||||
if member.Decl != nil {
|
||||
if err := writeDecl(f, member.Decl); err != nil {
|
||||
return err
|
||||
}
|
||||
writeDecl(f, member.Decl)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeDecl(f *formatter, decl Decl) error {
|
||||
func writeDecl(f *formatter, decl Decl) {
|
||||
switch fn := decl.(type) {
|
||||
case *FnDecl:
|
||||
if err := f.Writef("fn %s(", fn.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeParams(f, fn.Params); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := f.WriteString(") "); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeTypeExpr(f, fn.ReturnType); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeBlock(f, fn.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Writef("fn %s(", fn.Name)
|
||||
writeParams(f, fn.Params)
|
||||
f.Writef(") ")
|
||||
writeTypeExpr(f, fn.ReturnType)
|
||||
writeBlock(f, fn.Body)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeParams(f *formatter, params []*ParamDecl) error {
|
||||
func writeParams(f *formatter, params []*ParamDecl) {
|
||||
for i, param := range params {
|
||||
if i > 0 {
|
||||
if _, err := f.WriteString(", "); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Writef(", ")
|
||||
}
|
||||
if param.Name != "" {
|
||||
if _, err := f.WriteString(param.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := f.WriteString(": "); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := writeTypeExpr(f, param.Type); err != nil {
|
||||
return err
|
||||
f.Writef("%s: ", param.Name)
|
||||
}
|
||||
writeTypeExpr(f, param.Type)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTypeExpr(f *formatter, typ TypeExpr) error {
|
||||
func writeTypeExpr(f *formatter, typ TypeExpr) {
|
||||
switch t := typ.(type) {
|
||||
case *Identifier:
|
||||
_, err := f.WriteString(t.Name)
|
||||
return err
|
||||
f.Writef("%s", t.Name)
|
||||
case nil:
|
||||
return nil
|
||||
// nothing
|
||||
default:
|
||||
// fallback: print as string
|
||||
_, err := f.WriteString(fmt.Sprintf("%v", t))
|
||||
return err
|
||||
f.Writef("%v", t)
|
||||
}
|
||||
}
|
||||
|
||||
func writeBlock(f *formatter, block *Block) error {
|
||||
func writeBlock(f *formatter, block *Block) {
|
||||
if block == nil {
|
||||
_, err := f.WriteString(";")
|
||||
return err
|
||||
f.Writef(";")
|
||||
return
|
||||
}
|
||||
if _, err := f.WriteString(" {\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, stmt := range block.Stmts {
|
||||
if err := writeStmt(f, stmt); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := f.WriteString("\n"); err != nil {
|
||||
return err
|
||||
f.Writef(" {\n")
|
||||
f.indent++
|
||||
for i, stmt := range block.Stmts {
|
||||
f.writeIndent()
|
||||
writeStmt(f, stmt)
|
||||
if i < len(block.Stmts)-1 {
|
||||
f.Writef("\n")
|
||||
}
|
||||
}
|
||||
if _, err := f.WriteString("}\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
f.indent--
|
||||
f.Writef("\n")
|
||||
f.writeIndent()
|
||||
f.Writef("}\n")
|
||||
}
|
||||
|
||||
func writeStmt(f *formatter, stmt Stmt) error {
|
||||
func writeStmt(f *formatter, stmt Stmt) {
|
||||
switch stmt.(type) {
|
||||
case *ReturnStmt:
|
||||
if _, err := f.WriteString("return;"); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Writef("return;")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ func TestHelloWorld(t *testing.T) {
|
||||
expected := `//! Hello, world!
|
||||
|
||||
fn main() void {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
`
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user