From 1c09ddffeaf39c6ab40bcb161019b2f6aa6f29fa Mon Sep 17 00:00:00 2001 From: "J. Ritchie Carroll" Date: Sun, 13 Oct 2024 07:04:32 -0400 Subject: [PATCH] Added labeled branch targets for for loops --- .../Behavioral/ForVariants/ForVariants.cs | 19 ++++++-- .../Behavioral/ForVariants/ForVariants.go | 7 +++ src/go2cs2/main.go | 26 ++++------- src/go2cs2/visitBlockStmt.go | 40 +++++----------- src/go2cs2/visitBranchStmt.go | 21 ++++++++- src/go2cs2/visitForStmt.go | 14 ++++-- src/go2cs2/visitFuncDecl.go | 9 ++-- src/go2cs2/visitIfStmt.go | 31 ++++++++++++- src/go2cs2/visitLabeledStmt.go | 10 +++- src/go2cs2/visitStmt.go | 46 +++++++++++++++++-- 10 files changed, 157 insertions(+), 66 deletions(-) diff --git a/src/Tests/Behavioral/ForVariants/ForVariants.cs b/src/Tests/Behavioral/ForVariants/ForVariants.cs index 040f8528b..a742541bb 100644 --- a/src/Tests/Behavioral/ForVariants/ForVariants.cs +++ b/src/Tests/Behavioral/ForVariants/ForVariants.cs @@ -34,6 +34,7 @@ private static void Main() { fmt.Println("i =", i); fmt.Println(); +@out: ref var iɅ1 = ref heap(new nint(), out var ᏑiɅ1); for (iɅ1 = 0; iɅ1 < 5; iɅ1++) { @@ -44,12 +45,20 @@ private static void Main() { for (iɅ2 = 12; iɅ2 < 15; iɅ2++) { f(ᏑiɅ2); + goto @out_break; } //c + if (iɅ1 > 13) + { + goto @out_continue; + } + fmt.Println(); - } +@out_continue:; + } +@out_break:; //d fmt.Println(); fmt.Println("i =", i); @@ -57,9 +66,11 @@ private static void Main() { while (true) { i++; - f(Ꮡi); /* visitIfStmt: if i > 12 { - break -} */ + f(Ꮡi); + if (i > 12) + { + break; + } } diff --git a/src/Tests/Behavioral/ForVariants/ForVariants.go b/src/Tests/Behavioral/ForVariants/ForVariants.go index 52562bd8f..766b67596 100644 --- a/src/Tests/Behavioral/ForVariants/ForVariants.go +++ b/src/Tests/Behavioral/ForVariants/ForVariants.go @@ -31,13 +31,20 @@ func main() { fmt.Println("i =", i) fmt.Println() +out: for i := 0; i < 5; i++ { // a f(&i) // b for i := 12; i < 15; i++ { f(&i) + break out } //c + + if i > 13 { + continue out + } + fmt.Println() } //d diff --git a/src/go2cs2/main.go b/src/go2cs2/main.go index 06b45ae15..90d142a03 100644 --- a/src/go2cs2/main.go +++ b/src/go2cs2/main.go @@ -65,16 +65,12 @@ type Visitor struct { tempVarCount map[string]int // BlockStmt variables - blocks Stack[*strings.Builder] - blockInnerPrefixInjection Stack[string] - blockInnerSuffixInjection Stack[string] - blockOuterPrefixInjection Stack[string] - blockOuterSuffixInjection Stack[string] - firstStatementIsReturn bool - identEscapesHeap map[*ast.Ident]bool - identNames map[*ast.Ident]string // Local identifiers to adjusted names map - isReassigned map[*ast.Ident]bool // Local identifiers to reassignment status map - scopeStack []map[string]*types.Var // Stack of local variable scopes + blocks Stack[*strings.Builder] + firstStatementIsReturn bool + identEscapesHeap map[*ast.Ident]bool + identNames map[*ast.Ident]string // Local identifiers to adjusted names map + isReassigned map[*ast.Ident]bool // Local identifiers to reassignment status map + scopeStack []map[string]*types.Var // Stack of local variable scopes } const RootNamespace = "go" @@ -280,14 +276,8 @@ Examples: options: options, globalIdentNames: globalIdentNames, globalScope: globalScope, - - // BlockStmt variable initializations - blocks: Stack[*strings.Builder]{}, - blockInnerPrefixInjection: Stack[string]{}, - blockInnerSuffixInjection: Stack[string]{}, - blockOuterPrefixInjection: Stack[string]{}, - blockOuterSuffixInjection: Stack[string]{}, - identEscapesHeap: map[*ast.Ident]bool{}, + blocks: Stack[*strings.Builder]{}, + identEscapesHeap: map[*ast.Ident]bool{}, } visitor.visitFile(fileEntry.file) diff --git a/src/go2cs2/visitBlockStmt.go b/src/go2cs2/visitBlockStmt.go index 431bf83e2..ae9e72759 100644 --- a/src/go2cs2/visitBlockStmt.go +++ b/src/go2cs2/visitBlockStmt.go @@ -5,14 +5,14 @@ import ( "strings" ) -func (v *Visitor) visitBlockStmt(blockStmt *ast.BlockStmt, format FormattingContext) { +func (v *Visitor) visitBlockStmt(blockStmt *ast.BlockStmt, context BlockStmtContext) { v.pushBlock() - if v.blockOuterPrefixInjection.Len() > 0 { - v.targetFile.WriteString(v.blockOuterPrefixInjection.Pop()) + if len(context.outerPrefix) > 0 { + v.targetFile.WriteString(context.outerPrefix) } - if format.useNewLine { + if context.format.useNewLine { v.targetFile.WriteString(v.newline) v.targetFile.WriteString(v.indent(v.indentLevel)) v.targetFile.WriteString("{") @@ -20,13 +20,13 @@ func (v *Visitor) visitBlockStmt(blockStmt *ast.BlockStmt, format FormattingCont v.targetFile.WriteString(" {") } - if v.blockInnerPrefixInjection.Len() > 0 { - v.targetFile.WriteString(v.blockInnerPrefixInjection.Pop()) + if len(context.innerPrefix) > 0 { + v.targetFile.WriteString(context.innerPrefix) } v.firstStatementIsReturn = false - if format.useIndent { + if context.format.useIndent { v.indentLevel++ } @@ -67,7 +67,7 @@ func (v *Visitor) visitBlockStmt(blockStmt *ast.BlockStmt, format FormattingCont lastStmt = stmt } - if format.useIndent { + if context.format.useIndent { v.indentLevel-- } @@ -79,15 +79,15 @@ func (v *Visitor) visitBlockStmt(blockStmt *ast.BlockStmt, format FormattingCont v.firstStatementIsReturn = ok } - if v.blockInnerSuffixInjection.Len() > 0 { - v.targetFile.WriteString(v.blockInnerSuffixInjection.Pop()) + if len(context.innerSuffix) > 0 { + v.targetFile.WriteString(context.innerSuffix) } v.targetFile.WriteString(v.newline) v.writeOutputLn("}") - if v.blockOuterSuffixInjection.Len() > 0 { - v.targetFile.WriteString(v.blockOuterSuffixInjection.Pop()) + if len(context.outerSuffix) > 0 { + v.targetFile.WriteString(context.outerSuffix) } // if (!m_firstTopLevelDeclaration && IndentLevel > 2) @@ -117,19 +117,3 @@ func (v *Visitor) popBlockAppend(appendToPrevious bool) string { return block } - -func (v *Visitor) pushInnerBlockPrefix(prefix string) { - v.blockInnerPrefixInjection.Push(prefix) -} - -func (v *Visitor) pushInnerBlockSuffix(suffix string) { - v.blockInnerSuffixInjection.Push(suffix) -} - -func (v *Visitor) pushOuterBlockPrefix(prefix string) { - v.blockOuterPrefixInjection.Push(prefix) -} - -func (v *Visitor) pushOuterBlockSuffix(suffix string) { - v.blockOuterSuffixInjection.Push(suffix) -} diff --git a/src/go2cs2/visitBranchStmt.go b/src/go2cs2/visitBranchStmt.go index 3fbc07507..e881f08bf 100644 --- a/src/go2cs2/visitBranchStmt.go +++ b/src/go2cs2/visitBranchStmt.go @@ -6,7 +6,24 @@ import ( ) func (v *Visitor) visitBranchStmt(branchStmt *ast.BranchStmt) { - if branchStmt.Tok != token.FALLTHROUGH { - v.writeOutputLn("/* visitBranchStmt: " + v.getPrintedNode(branchStmt) + " */") + // FALLTHROUGH is handled in visitSwitchStmt.go + switch branchStmt.Tok { + case token.BREAK: + v.targetFile.WriteString(v.newline) + if branchStmt.Label == nil { + v.writeOutput("break;") + } else { + v.writeOutput("goto " + getSanitizedIdentifier(branchStmt.Label.Name) + "_break;") + } + case token.CONTINUE: + v.targetFile.WriteString(v.newline) + if branchStmt.Label == nil { + v.writeOutput("continue;") + } else { + v.writeOutput("goto " + getSanitizedIdentifier(branchStmt.Label.Name) + "_continue;") + } + case token.GOTO: + v.targetFile.WriteString(v.newline) + v.writeOutput("goto " + getSanitizedIdentifier(branchStmt.Label.Name) + ";") } } diff --git a/src/go2cs2/visitForStmt.go b/src/go2cs2/visitForStmt.go index d52d6c865..e196a11ce 100644 --- a/src/go2cs2/visitForStmt.go +++ b/src/go2cs2/visitForStmt.go @@ -1,13 +1,14 @@ package main import ( + "fmt" "go/ast" "strings" ) const ForVarInitMarker = ">>MARKER:FOR_VAR_INIT<<" -func (v *Visitor) visitForStmt(forStmt *ast.ForStmt) { +func (v *Visitor) visitForStmt(forStmt *ast.ForStmt, target LabeledStmtContext) { v.targetFile.WriteString(v.newline) // Handle while-style for loops @@ -21,7 +22,7 @@ func (v *Visitor) visitForStmt(forStmt *ast.ForStmt) { } v.targetFile.WriteString(") ") - v.visitBlockStmt(forStmt.Body, DefaultFormattingContext()) + v.visitBlockStmt(forStmt.Body, DefaultBlockStmtContext()) return } @@ -86,5 +87,12 @@ func (v *Visitor) visitForStmt(forStmt *ast.ForStmt) { v.targetFile.WriteString(") ") - v.visitBlockStmt(forStmt.Body, DefaultFormattingContext()) + blockContext := DefaultBlockStmtContext() + + if len(target.label) > 0 { + blockContext.innerSuffix = fmt.Sprintf("%s%s%s_continue:;", v.newline, v.newline, target.label) + blockContext.outerSuffix = fmt.Sprintf("%s_break:;", target.label) + } + + v.visitBlockStmt(forStmt.Body, blockContext) } diff --git a/src/go2cs2/visitFuncDecl.go b/src/go2cs2/visitFuncDecl.go index 6c43ac1eb..56d99b855 100644 --- a/src/go2cs2/visitFuncDecl.go +++ b/src/go2cs2/visitFuncDecl.go @@ -40,14 +40,15 @@ func (v *Visitor) visitFuncDecl(funcDecl *ast.FuncDecl) { functionParametersMarker := fmt.Sprintf(FunctionParametersMarker, goFunctionName) functionExecContextMarker := fmt.Sprintf(FunctionExecContextMarker, goFunctionName) - v.pushInnerBlockPrefix(fmt.Sprintf(FunctionBlockPrefixMarker, goFunctionName)) + + blockContext := DefaultBlockStmtContext() + blockContext.innerPrefix = fmt.Sprintf(FunctionBlockPrefixMarker, goFunctionName) v.writeOutput(fmt.Sprintf("%s static %s %s(%s)%s", getAccess(goFunctionName), generateResultSignature(signature), csFunctionName, functionParametersMarker, functionExecContextMarker)) if funcDecl.Body != nil { - format := DefaultFormattingContext() - format.useNewLine = false - v.visitBlockStmt(funcDecl.Body, format) + blockContext.format.useNewLine = false + v.visitBlockStmt(funcDecl.Body, blockContext) } signatureOnly := funcDecl.Body == nil diff --git a/src/go2cs2/visitIfStmt.go b/src/go2cs2/visitIfStmt.go index f21a22fa5..9c9aa9d04 100644 --- a/src/go2cs2/visitIfStmt.go +++ b/src/go2cs2/visitIfStmt.go @@ -4,6 +4,33 @@ import ( "go/ast" ) -func (v *Visitor) visitIfStmt(ifStmt *ast.IfStmt) { - v.writeOutputLn("/* visitIfStmt: " + v.getPrintedNode(ifStmt) + " */") +func (v *Visitor) visitIfStmt(ifStmt *ast.IfStmt, source ParentBlockContext) { + v.targetFile.WriteString(v.newline) + + if ifStmt.Init != nil { + // Any declared variable will be scoped to if statement, so create a sub-block for it + v.targetFile.WriteString(v.newline) + v.writeOutput("{") + v.indentLevel++ + + v.visitStmt(ifStmt.Init, []StmtContext{source}) + } + + v.writeOutput("if (") + v.targetFile.WriteString(v.convExpr(ifStmt.Cond, nil)) + v.targetFile.WriteString(") ") + + v.visitBlockStmt(ifStmt.Body, DefaultBlockStmtContext()) + + if ifStmt.Else != nil { + v.writeOutput(" else ") + switch elseStmt := ifStmt.Else.(type) { + case *ast.IfStmt: + v.visitIfStmt(elseStmt, source) + case *ast.BlockStmt: + v.visitBlockStmt(elseStmt, DefaultBlockStmtContext()) + default: + panic("Unexpected Else type: " + v.getPrintedNode(elseStmt)) + } + } } diff --git a/src/go2cs2/visitLabeledStmt.go b/src/go2cs2/visitLabeledStmt.go index f5e108e18..76befa3f4 100644 --- a/src/go2cs2/visitLabeledStmt.go +++ b/src/go2cs2/visitLabeledStmt.go @@ -5,5 +5,13 @@ import ( ) func (v *Visitor) visitLabeledStmt(labeledStmt *ast.LabeledStmt) { - v.writeOutputLn("/* " + v.getPrintedNode(labeledStmt) + " */") + v.targetFile.WriteString(v.newline) + + labelName := getSanitizedIdentifier(labeledStmt.Label.Name) + + v.targetFile.WriteString(labelName + ":") + + target := LabeledStmtContext{label: labelName} + + v.visitStmt(labeledStmt.Stmt, []StmtContext{target}) } diff --git a/src/go2cs2/visitStmt.go b/src/go2cs2/visitStmt.go index b537ccac9..c0e8b965d 100644 --- a/src/go2cs2/visitStmt.go +++ b/src/go2cs2/visitStmt.go @@ -44,6 +44,42 @@ func (c FormattingContext) getDefault() StmtContext { return DefaultFormattingContext() } +type BlockStmtContext struct { + format FormattingContext + innerPrefix string + innerSuffix string + outerPrefix string + outerSuffix string +} + +func DefaultBlockStmtContext() BlockStmtContext { + return BlockStmtContext{ + format: DefaultFormattingContext(), + innerPrefix: "", + innerSuffix: "", + outerPrefix: "", + outerSuffix: "", + } +} + +func (c BlockStmtContext) getDefault() StmtContext { + return DefaultBlockStmtContext() +} + +type LabeledStmtContext struct { + label string +} + +func DefaultLabeledStmtContext() LabeledStmtContext { + return LabeledStmtContext{ + label: "", + } +} + +func (c LabeledStmtContext) getDefault() StmtContext { + return DefaultLabeledStmtContext() +} + func getStmtContext[TContext StmtContext](contexts []StmtContext) TContext { var zeroValue TContext @@ -69,8 +105,8 @@ func (v *Visitor) visitStmt(stmt ast.Stmt, contexts []StmtContext) { format := getStmtContext[FormattingContext](contexts) v.visitAssignStmt(stmtType, source, format) case *ast.BlockStmt: - format := getStmtContext[FormattingContext](contexts) - v.visitBlockStmt(stmtType, format) + context := getStmtContext[BlockStmtContext](contexts) + v.visitBlockStmt(stmtType, context) case *ast.BranchStmt: v.visitBranchStmt(stmtType) case *ast.CommClause: @@ -85,11 +121,13 @@ func (v *Visitor) visitStmt(stmt ast.Stmt, contexts []StmtContext) { case *ast.ExprStmt: v.visitExprStmt(stmtType) case *ast.ForStmt: - v.visitForStmt(stmtType) + target := getStmtContext[LabeledStmtContext](contexts) + v.visitForStmt(stmtType, target) case *ast.GoStmt: v.visitGoStmt(stmtType) case *ast.IfStmt: - v.visitIfStmt(stmtType) + source := getStmtContext[ParentBlockContext](contexts) + v.visitIfStmt(stmtType, source) case *ast.IncDecStmt: format := getStmtContext[FormattingContext](contexts) v.visitIncDecStmt(stmtType, format)