package convert import ( "fmt" "git.maximhutz.com/max/lambda/pkg/debruijn" "git.maximhutz.com/max/lambda/pkg/lambda" ) // DeBruijnToLambda converts a De Bruijn expression back to named lambda calculus. func DeBruijnToLambda(expr debruijn.Expression) lambda.Expression { return deBruijnToLambda(expr, []string{}) } func deBruijnToLambda(expr debruijn.Expression, context []string) lambda.Expression { switch e := expr.(type) { case *debruijn.Variable: if e.Index() >= 0 && e.Index() < len(context) { return lambda.NewVariable(context[e.Index()]) } if e.Label() != "" { return lambda.NewVariable(e.Label()) } return lambda.NewVariable(fmt.Sprintf("free_%d", e.Index())) case *debruijn.Abstraction: paramName := generateParamName(context) newContext := append([]string{paramName}, context...) body := deBruijnToLambda(e.Body(), newContext) return lambda.NewAbstraction(paramName, body) case *debruijn.Application: abs := deBruijnToLambda(e.Abstraction(), context) arg := deBruijnToLambda(e.Argument(), context) return lambda.NewApplication(abs, arg) default: return nil } } // generateParamName generates a fresh parameter name that doesn't conflict with context. func generateParamName(context []string) string { base := 'a' for i := 0; ; i++ { name := string(rune(base + rune(i%26))) if i >= 26 { name = fmt.Sprintf("%s%d", name, i/26) } conflict := false for _, existing := range context { if existing == name { conflict = true break } } if !conflict { return name } } }