Add value-returning match expressions
This commit is contained in:
parent
b0a52de4ea
commit
ab783c33ed
12 changed files with 384 additions and 14 deletions
|
|
@ -1679,6 +1679,8 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
return receiver + "[" + index + "]", nil
|
||||
case MatchExpr:
|
||||
return g.valueMatch(e, expectedType)
|
||||
case EnumVariantExpr:
|
||||
if e.EnumName == "Result" {
|
||||
base, args, ok := parseGenericType(expectedType)
|
||||
|
|
@ -1743,6 +1745,128 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *goGenerator) valueMatch(match MatchExpr, expectedType string) (string, error) {
|
||||
enumName := strings.TrimPrefix(g.exprType(match.Value), "*")
|
||||
if enumName == "" && len(match.Cases) > 0 {
|
||||
enumName = match.Cases[0].EnumName
|
||||
}
|
||||
decl, ok := g.enums[enumName]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("match value is not a known enum")
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, c := range match.Cases {
|
||||
if c.EnumName != enumName {
|
||||
return "", fmt.Errorf("match case %s::%s does not match enum %s", c.EnumName, c.VariantName, enumName)
|
||||
}
|
||||
if seen[c.VariantName] {
|
||||
return "", fmt.Errorf("duplicate match case %s::%s", enumName, c.VariantName)
|
||||
}
|
||||
seen[c.VariantName] = true
|
||||
variant := enumVariant(decl, c.VariantName)
|
||||
if variant == nil {
|
||||
return "", fmt.Errorf("unknown variant %s::%s", enumName, c.VariantName)
|
||||
}
|
||||
if len(c.Bindings) != len(variant.PayloadTypes) {
|
||||
return "", fmt.Errorf("match case %s::%s expects %d bindings", enumName, c.VariantName, len(variant.PayloadTypes))
|
||||
}
|
||||
}
|
||||
for _, variant := range decl.Variants {
|
||||
if !seen[variant.Name] {
|
||||
return "", fmt.Errorf("non-exhaustive match for %s: missing %s", enumName, variant.Name)
|
||||
}
|
||||
}
|
||||
resultType := expectedType
|
||||
if resultType == "" || resultType == "Any" {
|
||||
for _, c := range match.Cases {
|
||||
variant := enumVariant(decl, c.VariantName)
|
||||
g.pushScope()
|
||||
for i, binding := range c.Bindings {
|
||||
g.defineType(binding, variant.PayloadTypes[i])
|
||||
}
|
||||
armType := g.exprType(c.Value)
|
||||
g.popScope()
|
||||
if armType != "" {
|
||||
resultType = armType
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if resultType == "" || resultType == "Unit" {
|
||||
return "", fmt.Errorf("match expression result type cannot be inferred")
|
||||
}
|
||||
for _, c := range match.Cases {
|
||||
variant := enumVariant(decl, c.VariantName)
|
||||
g.pushScope()
|
||||
for i, binding := range c.Bindings {
|
||||
g.defineType(binding, variant.PayloadTypes[i])
|
||||
}
|
||||
armType := g.exprType(c.Value)
|
||||
g.popScope()
|
||||
if armType != "" && armType != resultType {
|
||||
return "", fmt.Errorf("match expression arm %s::%s has type %s, expected %s", enumName, c.VariantName, armType, resultType)
|
||||
}
|
||||
}
|
||||
value, err := g.expr(match.Value, enumName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
g.matchCounter++
|
||||
matchName := fmt.Sprintf("gotlinMatch%d", g.matchCounter)
|
||||
var out strings.Builder
|
||||
out.WriteString("func() ")
|
||||
out.WriteString(mapGoType(resultType))
|
||||
out.WriteString(" { ")
|
||||
if enumIsString(decl) {
|
||||
out.WriteString("switch ")
|
||||
out.WriteString(value)
|
||||
out.WriteString(" { ")
|
||||
for _, c := range match.Cases {
|
||||
arm, err := g.expr(c.Value, resultType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out.WriteString("case ")
|
||||
out.WriteString(enumName + c.VariantName)
|
||||
out.WriteString(": return ")
|
||||
out.WriteString(arm)
|
||||
out.WriteString("; ")
|
||||
}
|
||||
out.WriteString("}; ")
|
||||
} else {
|
||||
out.WriteString("switch ")
|
||||
out.WriteString(matchName)
|
||||
out.WriteString(" := ")
|
||||
out.WriteString(value)
|
||||
out.WriteString(".(type) { ")
|
||||
for _, c := range match.Cases {
|
||||
variant := enumVariant(decl, c.VariantName)
|
||||
g.pushScope()
|
||||
out.WriteString("case *")
|
||||
out.WriteString(enumName + c.VariantName)
|
||||
out.WriteString(": ")
|
||||
for i, binding := range c.Bindings {
|
||||
g.defineType(binding, variant.PayloadTypes[i])
|
||||
out.WriteString(binding)
|
||||
out.WriteString(" := ")
|
||||
out.WriteString(matchName)
|
||||
out.WriteString(fmt.Sprintf(".Value%d; ", i))
|
||||
}
|
||||
arm, err := g.expr(c.Value, resultType)
|
||||
g.popScope()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out.WriteString("return ")
|
||||
out.WriteString(arm)
|
||||
out.WriteString("; ")
|
||||
}
|
||||
out.WriteString("}; ")
|
||||
}
|
||||
out.WriteString(`panic("unreachable exhaustive match") }()`)
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
func (g *goGenerator) emitJSONDecodeSupport() {
|
||||
g.line("func gotlinJSONDecode[T any](body []byte) GotlinResult[T] {")
|
||||
g.indentLevel++
|
||||
|
|
@ -2883,6 +3007,22 @@ func exprUsesName(expr Expr, name string, shadowed bool) bool {
|
|||
}
|
||||
}
|
||||
return false
|
||||
case MatchExpr:
|
||||
if exprUsesName(e.Value, name, shadowed) {
|
||||
return true
|
||||
}
|
||||
for _, matchCase := range e.Cases {
|
||||
caseShadowed := shadowed
|
||||
for _, binding := range matchCase.Bindings {
|
||||
if binding == name {
|
||||
caseShadowed = true
|
||||
}
|
||||
}
|
||||
if exprUsesName(matchCase.Value, name, caseShadowed) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case LambdaExpr:
|
||||
lambdaShadowed := shadowed
|
||||
if e.ImplicitIt && name == "it" {
|
||||
|
|
@ -3044,6 +3184,12 @@ func (g *goGenerator) exprType(expr Expr) string {
|
|||
if _, args, ok := parseGenericType(g.exprType(e.Receiver)); ok && len(args) > 0 {
|
||||
return args[len(args)-1]
|
||||
}
|
||||
case MatchExpr:
|
||||
for _, c := range e.Cases {
|
||||
if typ := g.exprType(c.Value); typ != "" {
|
||||
return typ
|
||||
}
|
||||
}
|
||||
case EnumVariantExpr:
|
||||
return e.EnumName
|
||||
case NonNullExpr:
|
||||
|
|
@ -3280,6 +3426,15 @@ func exprUsesPrintln(expr Expr) bool {
|
|||
return exprUsesPrintln(e.Left) || exprUsesPrintln(e.Right)
|
||||
case SelectorExpr:
|
||||
return exprUsesPrintln(e.Receiver)
|
||||
case MatchExpr:
|
||||
if exprUsesPrintln(e.Value) {
|
||||
return true
|
||||
}
|
||||
for _, matchCase := range e.Cases {
|
||||
if exprUsesPrintln(matchCase.Value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case LambdaExpr:
|
||||
return usesPrintln(e.Body)
|
||||
}
|
||||
|
|
@ -3306,6 +3461,15 @@ func exprUsesRunCatching(expr Expr) bool {
|
|||
return exprUsesRunCatching(e.Left) || exprUsesRunCatching(e.Right)
|
||||
case SelectorExpr:
|
||||
return exprUsesRunCatching(e.Receiver)
|
||||
case MatchExpr:
|
||||
if exprUsesRunCatching(e.Value) {
|
||||
return true
|
||||
}
|
||||
for _, matchCase := range e.Cases {
|
||||
if exprUsesRunCatching(matchCase.Value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case LambdaExpr:
|
||||
return usesRunCatching(e.Body)
|
||||
}
|
||||
|
|
@ -3332,6 +3496,15 @@ func exprUsesTimerBuiltins(expr Expr) bool {
|
|||
return exprUsesTimerBuiltins(e.Left) || exprUsesTimerBuiltins(e.Right)
|
||||
case SelectorExpr:
|
||||
return exprUsesTimerBuiltins(e.Receiver)
|
||||
case MatchExpr:
|
||||
if exprUsesTimerBuiltins(e.Value) {
|
||||
return true
|
||||
}
|
||||
for _, matchCase := range e.Cases {
|
||||
if exprUsesTimerBuiltins(matchCase.Value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case LambdaExpr:
|
||||
return usesTimerBuiltins(e.Body)
|
||||
}
|
||||
|
|
@ -3358,6 +3531,15 @@ func exprUsesEveryBuiltin(expr Expr) bool {
|
|||
return exprUsesEveryBuiltin(e.Left) || exprUsesEveryBuiltin(e.Right)
|
||||
case SelectorExpr:
|
||||
return exprUsesEveryBuiltin(e.Receiver)
|
||||
case MatchExpr:
|
||||
if exprUsesEveryBuiltin(e.Value) {
|
||||
return true
|
||||
}
|
||||
for _, matchCase := range e.Cases {
|
||||
if exprUsesEveryBuiltin(matchCase.Value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case LambdaExpr:
|
||||
return usesEveryBuiltin(e.Body)
|
||||
}
|
||||
|
|
@ -3485,6 +3667,11 @@ func usedImportAliases(program *Program) map[string]bool {
|
|||
for _, value := range e.Values {
|
||||
walkExpr(value)
|
||||
}
|
||||
case MatchExpr:
|
||||
walkExpr(e.Value)
|
||||
for _, matchCase := range e.Cases {
|
||||
walkExpr(matchCase.Value)
|
||||
}
|
||||
case LambdaExpr:
|
||||
for _, param := range e.Params {
|
||||
markType(param.Type)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue