Add expression functions with context boundaries

This commit is contained in:
pavel 2026-08-27 23:05:49 +02:00
commit 773c34f3f4
18 changed files with 391 additions and 52 deletions

View file

@ -84,7 +84,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
g.line("")
for _, fn := range program.Functions {
if usesPrintln(fn.Body) {
if usesPrintln(fn.Body) || (fn.ExpressionBody != nil && exprUsesPrintln(fn.ExpressionBody)) {
g.needsFmt = true
break
}
@ -92,7 +92,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
if !g.needsFmt {
for _, class := range program.Classes {
for _, method := range class.Methods {
if usesPrintln(method.Body) {
if usesPrintln(method.Body) || (method.ExpressionBody != nil && exprUsesPrintln(method.ExpressionBody)) {
g.needsFmt = true
break
}
@ -104,7 +104,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
}
if !g.needsRunCatch {
for _, fn := range program.Functions {
if usesRunCatching(fn.Body) {
if usesRunCatching(fn.Body) || (fn.ExpressionBody != nil && exprUsesRunCatching(fn.ExpressionBody)) {
g.needsRunCatch = true
break
}
@ -112,7 +112,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
}
if !g.needsGoUnwrap {
for _, fn := range program.Functions {
if usesGoUnwrap(fn.Body) {
if usesGoUnwrap(fn.Body) || fn.ExpressionBody != nil {
g.needsGoUnwrap = true
break
}
@ -121,7 +121,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
if !g.needsGoUnwrap {
for _, class := range program.Classes {
for _, method := range class.Methods {
if usesGoUnwrap(method.Body) {
if usesGoUnwrap(method.Body) || method.ExpressionBody != nil {
g.needsGoUnwrap = true
break
}
@ -134,7 +134,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
if !g.needsRunCatch {
for _, class := range program.Classes {
for _, method := range class.Methods {
if usesRunCatching(method.Body) {
if usesRunCatching(method.Body) || (method.ExpressionBody != nil && exprUsesRunCatching(method.ExpressionBody)) {
g.needsRunCatch = true
break
}
@ -277,13 +277,24 @@ func (g *goGenerator) function(fn FunctionDecl) error {
g.write(fn.Name)
g.write(renderGoTypeParameters(fn.TypeParams))
g.write(g.renderGoFunctionParams(fn))
if ret := g.goReturnType(fn.ReturnType); ret != "" {
returnType := g.functionReturnType(fn)
if ret := g.goReturnType(returnType); ret != "" {
g.write(" ")
g.write(ret)
}
g.write(" {\n")
g.indentLevel++
if err := g.block(fn.Body); err != nil {
if fn.ExpressionBody != nil {
value, err := g.expr(fn.ExpressionBody, returnType)
if err != nil {
return fmt.Errorf("function %s: %w", fn.Name, err)
}
if g.goReturnType(returnType) == "" {
g.line(value)
} else {
g.line("return " + value)
}
} else if err := g.block(fn.Body); err != nil {
return fmt.Errorf("function %s: %w", fn.Name, err)
}
g.indentLevel--
@ -443,13 +454,24 @@ func (g *goGenerator) method(class ClassDecl, fn FunctionDecl) error {
g.write(") ")
g.write(fn.Name)
g.write(g.renderGoFunctionParams(fn))
if ret := g.goReturnType(fn.ReturnType); ret != "" {
returnType := g.functionReturnType(fn)
if ret := g.goReturnType(returnType); ret != "" {
g.write(" ")
g.write(ret)
}
g.write(" {\n")
g.indentLevel++
if err := g.block(fn.Body); err != nil {
if fn.ExpressionBody != nil {
value, err := g.expr(fn.ExpressionBody, returnType)
if err != nil {
return fmt.Errorf("method %s.%s: %w", class.Name, fn.Name, err)
}
if g.goReturnType(returnType) == "" {
g.line(value)
} else {
g.line("return " + value)
}
} else if err := g.block(fn.Body); err != nil {
return fmt.Errorf("method %s.%s: %w", class.Name, fn.Name, err)
}
g.indentLevel--
@ -898,6 +920,26 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
return "", err
}
return "gotlinRunBlocking(" + block + ")", nil
case "withContext":
if len(e.Args) != 2 {
return "", fmt.Errorf("withContext expects a Go context and a lambda")
}
ctx, err := g.expr(e.Args[0], "context.Context")
if err != nil {
return "", err
}
lambda, ok := e.Args[1].(LambdaExpr)
if !ok {
return "", fmt.Errorf("withContext expects a lambda")
}
block, err := g.coroutineLambda(lambda, "Unit")
if err != nil {
return "", err
}
if g.currentCoroutineScope == "" {
return "gotlinRunWithContext(" + ctx + ", " + block + ")", nil
}
return g.currentCoroutineScope + ".WithContext(" + ctx + ", " + block + ")", nil
case "coroutineScope", "launch":
if g.currentCoroutineScope == "" {
return "", fmt.Errorf("%s requires a coroutine scope", ident.Name)
@ -2521,6 +2563,24 @@ func (g *goGenerator) hasCoroutineEffect(function FunctionDecl) bool {
return g.semantic.FunctionEffects[key].Has(CoroutineEffect)
}
func (g *goGenerator) functionReturnType(function FunctionDecl) string {
if !function.InferReturn {
return function.ReturnType
}
var symbol *Symbol
if g.currentClass != nil {
symbol = g.semantic.ClassInfo[g.currentClass.Name].Method(function.Name)
} else {
symbol, _ = g.semantic.Global.Lookup(function.Name)
}
if symbol != nil {
if signature, ok := symbol.Type.(FunctionType); ok {
return signature.Result.String()
}
}
return function.ReturnType
}
func renderGoTypeParameters(params []string) string {
if len(params) == 0 {
return ""
@ -2855,6 +2915,9 @@ func usedImportAliases(program *Program) map[string]bool {
markType(param.Type)
}
markType(method.ReturnType)
if method.ExpressionBody != nil {
walkExpr(method.ExpressionBody)
}
for _, stmt := range method.Body {
walkStmt(stmt)
}
@ -2865,6 +2928,9 @@ func usedImportAliases(program *Program) map[string]bool {
markType(param.Type)
}
markType(fn.ReturnType)
if fn.ExpressionBody != nil {
walkExpr(fn.ExpressionBody)
}
for _, stmt := range fn.Body {
walkStmt(stmt)
}