Use dot notation for enum variants
This commit is contained in:
parent
ab783c33ed
commit
117d188194
13 changed files with 162 additions and 154 deletions
|
|
@ -109,9 +109,9 @@ enum PaymentResult {
|
||||||
|
|
||||||
fun describe(result: PaymentResult): String {
|
fun describe(result: PaymentResult): String {
|
||||||
return match (result) {
|
return match (result) {
|
||||||
PaymentResult::Accepted(id) -> id
|
PaymentResult.Accepted(id) -> id
|
||||||
PaymentResult::Rejected(reason) -> reason
|
PaymentResult.Rejected(reason) -> reason
|
||||||
PaymentResult::Pending -> "pending"
|
PaymentResult.Pending -> "pending"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -646,7 +646,7 @@ func indexSymbols(text string, program *lang.Program) []symbol {
|
||||||
}
|
}
|
||||||
symbols = append(symbols, symbol{Name: decl.Name, Kind: symbolKindEnum, Detail: "enum " + decl.Name, Range: r})
|
symbols = append(symbols, symbol{Name: decl.Name, Kind: symbolKindEnum, Detail: "enum " + decl.Name, Range: r})
|
||||||
for _, variant := range decl.Variants {
|
for _, variant := range decl.Variants {
|
||||||
symbols = append(symbols, symbol{Name: variant.Name, Kind: symbolKindVariable, Detail: decl.Name + "::" + variant.Name, Range: r, Targets: []string{decl.Name}})
|
symbols = append(symbols, symbol{Name: variant.Name, Kind: symbolKindVariable, Detail: decl.Name + "." + variant.Name, Range: r, Targets: []string{decl.Name}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ enum PaymentResult {
|
||||||
|
|
||||||
fun describe(result: PaymentResult): String {
|
fun describe(result: PaymentResult): String {
|
||||||
return match (result) {
|
return match (result) {
|
||||||
PaymentResult::Accepted(id) -> "accepted " + id
|
PaymentResult.Accepted(id) -> "accepted " + id
|
||||||
PaymentResult::Rejected(reason) -> "rejected " + reason
|
PaymentResult.Rejected(reason) -> "rejected " + reason
|
||||||
PaymentResult::Pending -> "pending"
|
PaymentResult.Pending -> "pending"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
val result = PaymentResult::Accepted("payment-1")
|
val result = PaymentResult.Accepted("payment-1")
|
||||||
println(describe(result))
|
println(describe(result))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,12 @@ data class AccountResponse(
|
||||||
)
|
)
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
val entity = AccountEntity("account-1", AddressEntity("Berlin"), listOf("active"), EntityState::Active)
|
val entity = AccountEntity("account-1", AddressEntity("Berlin"), listOf("active"), EntityState.Active)
|
||||||
val response = entity.mapTo<AccountResponse>()
|
val response = entity.mapTo<AccountResponse>()
|
||||||
println(response.address.city)
|
println(response.address.city)
|
||||||
match (response.state) {
|
match (response.state) {
|
||||||
ResponseState::Active -> { println("active") }
|
ResponseState.Active -> { println("active") }
|
||||||
ResponseState::Failed(reason) -> { println(reason) }
|
ResponseState.Failed(reason) -> { println(reason) }
|
||||||
ResponseState::Pending -> { println("pending") }
|
ResponseState.Pending -> { println("pending") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,13 @@ enum PaymentResult { Accepted(String), Rejected(String), Pending }
|
||||||
fun describe(result: PaymentResult): String {
|
fun describe(result: PaymentResult): String {
|
||||||
var description = ""
|
var description = ""
|
||||||
match (result) {
|
match (result) {
|
||||||
PaymentResult::Accepted(id) -> { description = id }
|
PaymentResult.Accepted(id) -> { description = id }
|
||||||
PaymentResult::Rejected(reason) -> { description = reason }
|
PaymentResult.Rejected(reason) -> { description = reason }
|
||||||
PaymentResult::Pending -> { description = "pending" }
|
PaymentResult.Pending -> { description = "pending" }
|
||||||
}
|
}
|
||||||
return description
|
return description
|
||||||
}
|
}
|
||||||
fun main() { println(describe(PaymentResult::Accepted("p1"))) }
|
fun main() { println(describe(PaymentResult.Accepted("p1"))) }
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -37,7 +37,7 @@ fun main() { println(describe(PaymentResult::Accepted("p1"))) }
|
||||||
func TestRejectNonExhaustiveEnumMatch(t *testing.T) {
|
func TestRejectNonExhaustiveEnumMatch(t *testing.T) {
|
||||||
prog, err := Parse(`package demo
|
prog, err := Parse(`package demo
|
||||||
enum Result { Ok, Error(String) }
|
enum Result { Ok, Error(String) }
|
||||||
fun use(result: Result) { match (result) { Result::Ok -> { println("ok") } } }`)
|
fun use(result: Result) { match (result) { Result.Ok -> { println("ok") } } }`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ fun use(result: Result) { match (result) { Result::Ok -> { println("ok") } } }`)
|
||||||
func TestRejectWrongVariantPayloadCount(t *testing.T) {
|
func TestRejectWrongVariantPayloadCount(t *testing.T) {
|
||||||
prog, err := Parse(`package demo
|
prog, err := Parse(`package demo
|
||||||
enum Outcome { Ok(String) }
|
enum Outcome { Ok(String) }
|
||||||
fun main() { val result = Outcome::Ok() }`)
|
fun main() { val result = Outcome.Ok() }`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -60,10 +60,17 @@ fun main() { val result = Outcome::Ok() }`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRejectDoubleColonEnumSyntax(t *testing.T) {
|
||||||
|
_, err := Parse(`package demo enum State { Ready } fun main() { println(State::Ready) }`)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("deprecated double-colon enum syntax parsed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPayloadlessEnumUsesExactVariantStrings(t *testing.T) {
|
func TestPayloadlessEnumUsesExactVariantStrings(t *testing.T) {
|
||||||
prog, err := Parse(`package demo
|
prog, err := Parse(`package demo
|
||||||
enum Status { PendingReservation, Initiated }
|
enum Status { PendingReservation, Initiated }
|
||||||
fun main() { println(Status::PendingReservation) }`)
|
fun main() { println(Status.PendingReservation) }`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -83,8 +90,8 @@ func TestGenerateExhaustiveMatchExpression(t *testing.T) {
|
||||||
enum AccountType { BASIC, SAVINGS }
|
enum AccountType { BASIC, SAVINGS }
|
||||||
fun interestRate(accountType: AccountType): Double {
|
fun interestRate(accountType: AccountType): Double {
|
||||||
return match (accountType) {
|
return match (accountType) {
|
||||||
AccountType::BASIC -> 0.0
|
AccountType.BASIC -> 0.0
|
||||||
AccountType::SAVINGS -> 0.02
|
AccountType.SAVINGS -> 0.02
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -106,8 +113,8 @@ func TestGeneratePayloadMatchExpression(t *testing.T) {
|
||||||
enum Outcome { Success(String), Failure(String) }
|
enum Outcome { Success(String), Failure(String) }
|
||||||
fun message(outcome: Outcome): String {
|
fun message(outcome: Outcome): String {
|
||||||
return match (outcome) {
|
return match (outcome) {
|
||||||
Outcome::Success(value) -> value
|
Outcome.Success(value) -> value
|
||||||
Outcome::Failure(reason) -> reason
|
Outcome.Failure(reason) -> reason
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -129,8 +136,8 @@ func TestRejectInvalidMatchExpression(t *testing.T) {
|
||||||
source string
|
source string
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{`package demo enum State { On, Off } fun value(state: State): Int { return match (state) { State::On -> 1 } }`, "missing Off"},
|
{`package demo enum State { On, Off } fun value(state: State): Int { return match (state) { State.On -> 1 } }`, "missing Off"},
|
||||||
{`package demo enum State { On, Off } fun value(state: State): Int { return match (state) { State::On -> 1 State::Off -> "off" } }`, "has type String, expected Int"},
|
{`package demo enum State { On, Off } fun value(state: State): Int { return match (state) { State.On -> 1 State.Off -> "off" } }`, "has type String, expected Int"},
|
||||||
} {
|
} {
|
||||||
prog, err := Parse(test.source)
|
prog, err := Parse(test.source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1051,18 +1051,18 @@ func (g *goGenerator) stmt(stmt Stmt, tail []Stmt) error {
|
||||||
seen := map[string]bool{}
|
seen := map[string]bool{}
|
||||||
for _, c := range s.Cases {
|
for _, c := range s.Cases {
|
||||||
if c.EnumName != enumName {
|
if c.EnumName != enumName {
|
||||||
return fmt.Errorf("match case %s::%s does not match enum %s", c.EnumName, c.VariantName, enumName)
|
return fmt.Errorf("match case %s.%s does not match enum %s", c.EnumName, c.VariantName, enumName)
|
||||||
}
|
}
|
||||||
if seen[c.VariantName] {
|
if seen[c.VariantName] {
|
||||||
return fmt.Errorf("duplicate match case %s::%s", enumName, c.VariantName)
|
return fmt.Errorf("duplicate match case %s.%s", enumName, c.VariantName)
|
||||||
}
|
}
|
||||||
seen[c.VariantName] = true
|
seen[c.VariantName] = true
|
||||||
variant := enumVariant(decl, c.VariantName)
|
variant := enumVariant(decl, c.VariantName)
|
||||||
if variant == nil {
|
if variant == nil {
|
||||||
return fmt.Errorf("unknown variant %s::%s", enumName, c.VariantName)
|
return fmt.Errorf("unknown variant %s.%s", enumName, c.VariantName)
|
||||||
}
|
}
|
||||||
if len(c.Bindings) != len(variant.PayloadTypes) {
|
if len(c.Bindings) != len(variant.PayloadTypes) {
|
||||||
return fmt.Errorf("match case %s::%s expects %d bindings", enumName, c.VariantName, len(variant.PayloadTypes))
|
return fmt.Errorf("match case %s.%s expects %d bindings", enumName, c.VariantName, len(variant.PayloadTypes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, variant := range decl.Variants {
|
for _, variant := range decl.Variants {
|
||||||
|
|
@ -1250,6 +1250,16 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s %s %s", left, e.Op, right), nil
|
return fmt.Sprintf("%s %s %s", left, e.Op, right), nil
|
||||||
case CallExpr:
|
case CallExpr:
|
||||||
|
if selector, ok := e.Callee.(SelectorExpr); ok {
|
||||||
|
if receiver, ok := selector.Receiver.(IdentExpr); ok {
|
||||||
|
if receiver.Name == "Result" {
|
||||||
|
return g.expr(EnumVariantExpr{EnumName: receiver.Name, VariantName: selector.Name, Values: e.Args}, expectedType)
|
||||||
|
}
|
||||||
|
if _, ok := g.enums[receiver.Name]; ok {
|
||||||
|
return g.expr(EnumVariantExpr{EnumName: receiver.Name, VariantName: selector.Name, Values: e.Args}, expectedType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if ident, ok := e.Callee.(IdentExpr); ok && coroutineBuiltins[ident.Name] {
|
if ident, ok := e.Callee.(IdentExpr); ok && coroutineBuiltins[ident.Name] {
|
||||||
switch ident.Name {
|
switch ident.Name {
|
||||||
case "runBlocking":
|
case "runBlocking":
|
||||||
|
|
@ -1607,6 +1617,18 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
|
||||||
}
|
}
|
||||||
return call, nil
|
return call, nil
|
||||||
case SelectorExpr:
|
case SelectorExpr:
|
||||||
|
if receiver, ok := e.Receiver.(IdentExpr); ok {
|
||||||
|
if decl, ok := g.enums[receiver.Name]; ok {
|
||||||
|
variant := enumVariant(decl, e.Name)
|
||||||
|
if variant == nil {
|
||||||
|
return "", fmt.Errorf("unknown variant %s.%s", receiver.Name, e.Name)
|
||||||
|
}
|
||||||
|
if len(variant.PayloadTypes) != 0 {
|
||||||
|
return "", fmt.Errorf("variant %s.%s requires %d values", receiver.Name, e.Name, len(variant.PayloadTypes))
|
||||||
|
}
|
||||||
|
return g.expr(EnumVariantExpr{EnumName: receiver.Name, VariantName: e.Name}, expectedType)
|
||||||
|
}
|
||||||
|
}
|
||||||
if receiverType := g.exprType(e.Receiver); strings.HasSuffix(receiverType, "?") {
|
if receiverType := g.exprType(e.Receiver); strings.HasSuffix(receiverType, "?") {
|
||||||
return "", fmt.Errorf("nullable receiver %s requires ?. or !! before .%s", receiverType, e.Name)
|
return "", fmt.Errorf("nullable receiver %s requires ?. or !! before .%s", receiverType, e.Name)
|
||||||
}
|
}
|
||||||
|
|
@ -1685,11 +1707,11 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
|
||||||
if e.EnumName == "Result" {
|
if e.EnumName == "Result" {
|
||||||
base, args, ok := parseGenericType(expectedType)
|
base, args, ok := parseGenericType(expectedType)
|
||||||
if !ok || base != "Result" || len(args) != 2 {
|
if !ok || base != "Result" || len(args) != 2 {
|
||||||
return "", fmt.Errorf("Result::%s requires an expected Result<T, Error> type", e.VariantName)
|
return "", fmt.Errorf("Result.%s requires an expected Result<T, Error> type", e.VariantName)
|
||||||
}
|
}
|
||||||
if e.VariantName == "Ok" {
|
if e.VariantName == "Ok" {
|
||||||
if len(e.Values) != 1 {
|
if len(e.Values) != 1 {
|
||||||
return "", fmt.Errorf("Result::Ok expects one value")
|
return "", fmt.Errorf("Result.Ok expects one value")
|
||||||
}
|
}
|
||||||
value, err := g.expr(e.Values[0], args[0])
|
value, err := g.expr(e.Values[0], args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1699,7 +1721,7 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
|
||||||
}
|
}
|
||||||
if e.VariantName == "Err" {
|
if e.VariantName == "Err" {
|
||||||
if len(e.Values) != 1 {
|
if len(e.Values) != 1 {
|
||||||
return "", fmt.Errorf("Result::Err expects one error")
|
return "", fmt.Errorf("Result.Err expects one error")
|
||||||
}
|
}
|
||||||
value, err := g.expr(e.Values[0], "Error")
|
value, err := g.expr(e.Values[0], "Error")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1721,10 +1743,10 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if variant == nil {
|
if variant == nil {
|
||||||
return "", fmt.Errorf("unknown variant %s::%s", e.EnumName, e.VariantName)
|
return "", fmt.Errorf("unknown variant %s.%s", e.EnumName, e.VariantName)
|
||||||
}
|
}
|
||||||
if len(e.Values) != len(variant.PayloadTypes) {
|
if len(e.Values) != len(variant.PayloadTypes) {
|
||||||
return "", fmt.Errorf("variant %s::%s expects %d values", e.EnumName, e.VariantName, len(variant.PayloadTypes))
|
return "", fmt.Errorf("variant %s.%s expects %d values", e.EnumName, e.VariantName, len(variant.PayloadTypes))
|
||||||
}
|
}
|
||||||
if enumIsString(decl) {
|
if enumIsString(decl) {
|
||||||
return e.EnumName + e.VariantName, nil
|
return e.EnumName + e.VariantName, nil
|
||||||
|
|
@ -1757,18 +1779,18 @@ func (g *goGenerator) valueMatch(match MatchExpr, expectedType string) (string,
|
||||||
seen := map[string]bool{}
|
seen := map[string]bool{}
|
||||||
for _, c := range match.Cases {
|
for _, c := range match.Cases {
|
||||||
if c.EnumName != enumName {
|
if c.EnumName != enumName {
|
||||||
return "", fmt.Errorf("match case %s::%s does not match enum %s", c.EnumName, c.VariantName, enumName)
|
return "", fmt.Errorf("match case %s.%s does not match enum %s", c.EnumName, c.VariantName, enumName)
|
||||||
}
|
}
|
||||||
if seen[c.VariantName] {
|
if seen[c.VariantName] {
|
||||||
return "", fmt.Errorf("duplicate match case %s::%s", enumName, c.VariantName)
|
return "", fmt.Errorf("duplicate match case %s.%s", enumName, c.VariantName)
|
||||||
}
|
}
|
||||||
seen[c.VariantName] = true
|
seen[c.VariantName] = true
|
||||||
variant := enumVariant(decl, c.VariantName)
|
variant := enumVariant(decl, c.VariantName)
|
||||||
if variant == nil {
|
if variant == nil {
|
||||||
return "", fmt.Errorf("unknown variant %s::%s", enumName, c.VariantName)
|
return "", fmt.Errorf("unknown variant %s.%s", enumName, c.VariantName)
|
||||||
}
|
}
|
||||||
if len(c.Bindings) != len(variant.PayloadTypes) {
|
if len(c.Bindings) != len(variant.PayloadTypes) {
|
||||||
return "", fmt.Errorf("match case %s::%s expects %d bindings", enumName, c.VariantName, len(variant.PayloadTypes))
|
return "", fmt.Errorf("match case %s.%s expects %d bindings", enumName, c.VariantName, len(variant.PayloadTypes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, variant := range decl.Variants {
|
for _, variant := range decl.Variants {
|
||||||
|
|
@ -1804,7 +1826,7 @@ func (g *goGenerator) valueMatch(match MatchExpr, expectedType string) (string,
|
||||||
armType := g.exprType(c.Value)
|
armType := g.exprType(c.Value)
|
||||||
g.popScope()
|
g.popScope()
|
||||||
if armType != "" && armType != resultType {
|
if armType != "" && armType != resultType {
|
||||||
return "", fmt.Errorf("match expression arm %s::%s has type %s, expected %s", enumName, c.VariantName, 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)
|
value, err := g.expr(match.Value, enumName)
|
||||||
|
|
@ -3110,6 +3132,13 @@ func (g *goGenerator) exprType(expr Expr) string {
|
||||||
case BoolExpr:
|
case BoolExpr:
|
||||||
return "Boolean"
|
return "Boolean"
|
||||||
case CallExpr:
|
case CallExpr:
|
||||||
|
if selector, ok := e.Callee.(SelectorExpr); ok {
|
||||||
|
if receiver, ok := selector.Receiver.(IdentExpr); ok {
|
||||||
|
if _, ok := g.enums[receiver.Name]; ok {
|
||||||
|
return receiver.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if ident, ok := e.Callee.(IdentExpr); ok && ident.Name == "keys" && len(e.Args) == 1 {
|
if ident, ok := e.Callee.(IdentExpr); ok && ident.Name == "keys" && len(e.Args) == 1 {
|
||||||
if _, args, ok := parseGenericType(g.exprType(e.Args[0])); ok && len(args) == 2 {
|
if _, args, ok := parseGenericType(g.exprType(e.Args[0])); ok && len(args) == 2 {
|
||||||
return "List<" + args[0] + ">"
|
return "List<" + args[0] + ">"
|
||||||
|
|
@ -3173,6 +3202,11 @@ func (g *goGenerator) exprType(expr Expr) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case SelectorExpr:
|
case SelectorExpr:
|
||||||
|
if receiver, ok := e.Receiver.(IdentExpr); ok {
|
||||||
|
if _, ok := g.enums[receiver.Name]; ok {
|
||||||
|
return receiver.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
if class, ok := g.classForType(g.exprType(e.Receiver)); ok {
|
if class, ok := g.classForType(g.exprType(e.Receiver)); ok {
|
||||||
for _, field := range class.Fields {
|
for _, field := range class.Fields {
|
||||||
if field.Name == e.Name {
|
if field.Name == e.Name {
|
||||||
|
|
|
||||||
|
|
@ -93,9 +93,6 @@ func (l *lexer) next() (token, error) {
|
||||||
case '.':
|
case '.':
|
||||||
return token{kind: tokenDot, lexeme: ".", pos: start}, nil
|
return token{kind: tokenDot, lexeme: ".", pos: start}, nil
|
||||||
case ':':
|
case ':':
|
||||||
if l.match(':') {
|
|
||||||
return token{kind: tokenDoubleColon, lexeme: "::", pos: start}, nil
|
|
||||||
}
|
|
||||||
return token{kind: tokenColon, lexeme: ":", pos: start}, nil
|
return token{kind: tokenColon, lexeme: ":", pos: start}, nil
|
||||||
case ';':
|
case ';':
|
||||||
return token{kind: tokenSemicolon, lexeme: ";", pos: start}, nil
|
return token{kind: tokenSemicolon, lexeme: ";", pos: start}, nil
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ func (g *goGenerator) validateMapping(source, target, path string, seen map[stri
|
||||||
}
|
}
|
||||||
for _, sourceVariant := range sourceEnum.Variants {
|
for _, sourceVariant := range sourceEnum.Variants {
|
||||||
targetVariant := enumVariant(targetEnum, sourceVariant.Name)
|
targetVariant := enumVariant(targetEnum, sourceVariant.Name)
|
||||||
variantPath := path + "::" + sourceVariant.Name
|
variantPath := path + "." + sourceVariant.Name
|
||||||
if targetVariant == nil {
|
if targetVariant == nil {
|
||||||
return fmt.Errorf("cannot map %s: target enum %s has no compatible variant", variantPath, targetEnum.Name)
|
return fmt.Errorf("cannot map %s: target enum %s has no compatible variant", variantPath, targetEnum.Name)
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +158,7 @@ func (g *goGenerator) emitMapping(pair mappingPair) error {
|
||||||
g.indentLevel++
|
g.indentLevel++
|
||||||
fields := make([]string, 0, len(sourceVariant.PayloadTypes))
|
fields := make([]string, 0, len(sourceVariant.PayloadTypes))
|
||||||
for i := range sourceVariant.PayloadTypes {
|
for i := range sourceVariant.PayloadTypes {
|
||||||
expr, err := g.mappingExpr(fmt.Sprintf("value.Value%d", i), sourceVariant.PayloadTypes[i], targetVariant.PayloadTypes[i], sourceEnum.Name+"::"+sourceVariant.Name)
|
expr, err := g.mappingExpr(fmt.Sprintf("value.Value%d", i), sourceVariant.PayloadTypes[i], targetVariant.PayloadTypes[i], sourceEnum.Name+"."+sourceVariant.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -751,7 +751,7 @@ func (p *parser) parseMatch() (Stmt, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if _, err := p.expect(tokenDoubleColon, "expected '::' in match case"); err != nil {
|
if _, err := p.expect(tokenDot, "expected '.' in match case"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
variant, err := p.expect(tokenIdent, "expected variant name")
|
variant, err := p.expect(tokenIdent, "expected variant name")
|
||||||
|
|
@ -813,7 +813,7 @@ func (p *parser) parseMatchExpr() (Expr, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if _, err := p.expect(tokenDoubleColon, "expected '::' in match case"); err != nil {
|
if _, err := p.expect(tokenDot, "expected '.' in match case"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
variant, err := p.expect(tokenIdent, "expected variant name")
|
variant, err := p.expect(tokenIdent, "expected variant name")
|
||||||
|
|
@ -1029,31 +1029,6 @@ func (p *parser) parsePrefix() (Expr, error) {
|
||||||
tok := p.advance()
|
tok := p.advance()
|
||||||
switch tok.kind {
|
switch tok.kind {
|
||||||
case tokenIdent:
|
case tokenIdent:
|
||||||
if p.match(tokenDoubleColon) {
|
|
||||||
variant, err := p.expect(tokenIdent, "expected enum variant")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var values []Expr
|
|
||||||
if p.match(tokenLParen) {
|
|
||||||
if !p.check(tokenRParen) {
|
|
||||||
for {
|
|
||||||
value, err := p.parseExpr(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
values = append(values, value)
|
|
||||||
if !p.match(tokenComma) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := p.expect(tokenRParen, "expected ')' after variant values"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p.parsePostfix(EnumVariantExpr{EnumName: tok.lexeme, VariantName: variant.lexeme, Values: values})
|
|
||||||
}
|
|
||||||
return p.parsePostfix(IdentExpr{Name: tok.lexeme})
|
return p.parsePostfix(IdentExpr{Name: tok.lexeme})
|
||||||
case tokenInt:
|
case tokenInt:
|
||||||
return IntExpr{Value: tok.lexeme}, nil
|
return IntExpr{Value: tok.lexeme}, nil
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ func TestResultQuestionPropagatesGoError(t *testing.T) {
|
||||||
import strconv
|
import strconv
|
||||||
fun parse(value: String): Result<Int, Error> {
|
fun parse(value: String): Result<Int, Error> {
|
||||||
val parsed = strconv.atoi(value)?
|
val parsed = strconv.atoi(value)?
|
||||||
return Result::Ok(parsed)
|
return Result.Ok(parsed)
|
||||||
}`)
|
}`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -74,8 +74,8 @@ fun changeDirectory(path: String): Result<Unit, Error> { return os.chdir(path) }
|
||||||
func TestResultQuestionPropagatesGotlinResult(t *testing.T) {
|
func TestResultQuestionPropagatesGotlinResult(t *testing.T) {
|
||||||
prog, err := Parse(`package demo
|
prog, err := Parse(`package demo
|
||||||
import errors
|
import errors
|
||||||
fun inner(ok: Boolean): Result<String, Error> { if (!ok) { return Result::Err(errors.new("failed")) }; return Result::Ok("ok") }
|
fun inner(ok: Boolean): Result<String, Error> { if (!ok) { return Result.Err(errors.new("failed")) }; return Result.Ok("ok") }
|
||||||
fun outer(): Result<String, Error> { val value = inner(true)?; return Result::Ok(value) }`)
|
fun outer(): Result<String, Error> { val value = inner(true)?; return Result.Ok(value) }`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +127,7 @@ import strconv
|
||||||
fun parse(value: String): Result<Int, Error> {
|
fun parse(value: String): Result<Int, Error> {
|
||||||
val input = value
|
val input = value
|
||||||
val parsed = strconv.atoi(input)?
|
val parsed = strconv.atoi(input)?
|
||||||
return Result::Ok(parsed)
|
return Result.Ok(parsed)
|
||||||
}`)
|
}`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -3,73 +3,72 @@ package lang
|
||||||
type tokenKind string
|
type tokenKind string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tokenEOF tokenKind = "EOF"
|
tokenEOF tokenKind = "EOF"
|
||||||
tokenIdent tokenKind = "IDENT"
|
tokenIdent tokenKind = "IDENT"
|
||||||
tokenInt tokenKind = "INT"
|
tokenInt tokenKind = "INT"
|
||||||
tokenFloat tokenKind = "FLOAT"
|
tokenFloat tokenKind = "FLOAT"
|
||||||
tokenString tokenKind = "STRING"
|
tokenString tokenKind = "STRING"
|
||||||
tokenTrue tokenKind = "TRUE"
|
tokenTrue tokenKind = "TRUE"
|
||||||
tokenFalse tokenKind = "FALSE"
|
tokenFalse tokenKind = "FALSE"
|
||||||
tokenNull tokenKind = "NULL"
|
tokenNull tokenKind = "NULL"
|
||||||
tokenImport tokenKind = "IMPORT"
|
tokenImport tokenKind = "IMPORT"
|
||||||
tokenPackage tokenKind = "PACKAGE"
|
tokenPackage tokenKind = "PACKAGE"
|
||||||
tokenClass tokenKind = "CLASS"
|
tokenClass tokenKind = "CLASS"
|
||||||
tokenData tokenKind = "DATA"
|
tokenData tokenKind = "DATA"
|
||||||
tokenWorker tokenKind = "WORKER"
|
tokenWorker tokenKind = "WORKER"
|
||||||
tokenInterface tokenKind = "INTERFACE"
|
tokenInterface tokenKind = "INTERFACE"
|
||||||
tokenEnum tokenKind = "ENUM"
|
tokenEnum tokenKind = "ENUM"
|
||||||
tokenMatch tokenKind = "MATCH"
|
tokenMatch tokenKind = "MATCH"
|
||||||
tokenFun tokenKind = "FUN"
|
tokenFun tokenKind = "FUN"
|
||||||
tokenSuspend tokenKind = "SUSPEND"
|
tokenSuspend tokenKind = "SUSPEND"
|
||||||
tokenOverride tokenKind = "OVERRIDE"
|
tokenOverride tokenKind = "OVERRIDE"
|
||||||
tokenPrivate tokenKind = "PRIVATE"
|
tokenPrivate tokenKind = "PRIVATE"
|
||||||
tokenVal tokenKind = "VAL"
|
tokenVal tokenKind = "VAL"
|
||||||
tokenVar tokenKind = "VAR"
|
tokenVar tokenKind = "VAR"
|
||||||
tokenIf tokenKind = "IF"
|
tokenIf tokenKind = "IF"
|
||||||
tokenElse tokenKind = "ELSE"
|
tokenElse tokenKind = "ELSE"
|
||||||
tokenWhile tokenKind = "WHILE"
|
tokenWhile tokenKind = "WHILE"
|
||||||
tokenFor tokenKind = "FOR"
|
tokenFor tokenKind = "FOR"
|
||||||
tokenIn tokenKind = "IN"
|
tokenIn tokenKind = "IN"
|
||||||
tokenSelect tokenKind = "SELECT"
|
tokenSelect tokenKind = "SELECT"
|
||||||
tokenReturn tokenKind = "RETURN"
|
tokenReturn tokenKind = "RETURN"
|
||||||
tokenGo tokenKind = "GO"
|
tokenGo tokenKind = "GO"
|
||||||
tokenDefer tokenKind = "DEFER"
|
tokenDefer tokenKind = "DEFER"
|
||||||
tokenTry tokenKind = "TRY"
|
tokenTry tokenKind = "TRY"
|
||||||
tokenCatch tokenKind = "CATCH"
|
tokenCatch tokenKind = "CATCH"
|
||||||
tokenThrow tokenKind = "THROW"
|
tokenThrow tokenKind = "THROW"
|
||||||
tokenLParen tokenKind = "("
|
tokenLParen tokenKind = "("
|
||||||
tokenRParen tokenKind = ")"
|
tokenRParen tokenKind = ")"
|
||||||
tokenLBrace tokenKind = "{"
|
tokenLBrace tokenKind = "{"
|
||||||
tokenRBrace tokenKind = "}"
|
tokenRBrace tokenKind = "}"
|
||||||
tokenLBracket tokenKind = "["
|
tokenLBracket tokenKind = "["
|
||||||
tokenRBracket tokenKind = "]"
|
tokenRBracket tokenKind = "]"
|
||||||
tokenComma tokenKind = ","
|
tokenComma tokenKind = ","
|
||||||
tokenDot tokenKind = "."
|
tokenDot tokenKind = "."
|
||||||
tokenColon tokenKind = ":"
|
tokenColon tokenKind = ":"
|
||||||
tokenDoubleColon tokenKind = "::"
|
tokenSemicolon tokenKind = ";"
|
||||||
tokenSemicolon tokenKind = ";"
|
tokenPlus tokenKind = "+"
|
||||||
tokenPlus tokenKind = "+"
|
tokenMinus tokenKind = "-"
|
||||||
tokenMinus tokenKind = "-"
|
tokenStar tokenKind = "*"
|
||||||
tokenStar tokenKind = "*"
|
tokenSlash tokenKind = "/"
|
||||||
tokenSlash tokenKind = "/"
|
tokenPercent tokenKind = "%"
|
||||||
tokenPercent tokenKind = "%"
|
tokenBang tokenKind = "!"
|
||||||
tokenBang tokenKind = "!"
|
tokenAssign tokenKind = "="
|
||||||
tokenAssign tokenKind = "="
|
tokenPlusAssign tokenKind = "+="
|
||||||
tokenPlusAssign tokenKind = "+="
|
tokenEq tokenKind = "=="
|
||||||
tokenEq tokenKind = "=="
|
tokenNeq tokenKind = "!="
|
||||||
tokenNeq tokenKind = "!="
|
tokenLt tokenKind = "<"
|
||||||
tokenLt tokenKind = "<"
|
tokenLte tokenKind = "<="
|
||||||
tokenLte tokenKind = "<="
|
tokenGt tokenKind = ">"
|
||||||
tokenGt tokenKind = ">"
|
tokenGte tokenKind = ">="
|
||||||
tokenGte tokenKind = ">="
|
tokenAnd tokenKind = "&&"
|
||||||
tokenAnd tokenKind = "&&"
|
tokenAmp tokenKind = "&"
|
||||||
tokenAmp tokenKind = "&"
|
tokenAt tokenKind = "@"
|
||||||
tokenAt tokenKind = "@"
|
tokenQuestion tokenKind = "?"
|
||||||
tokenQuestion tokenKind = "?"
|
tokenSafeDot tokenKind = "?."
|
||||||
tokenSafeDot tokenKind = "?."
|
tokenDoubleBang tokenKind = "!!"
|
||||||
tokenDoubleBang tokenKind = "!!"
|
tokenOr tokenKind = "||"
|
||||||
tokenOr tokenKind = "||"
|
tokenArrow tokenKind = "->"
|
||||||
tokenArrow tokenKind = "->"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var keywords = map[string]tokenKind{
|
var keywords = map[string]tokenKind{
|
||||||
|
|
|
||||||
|
|
@ -67,13 +67,13 @@
|
||||||
"prefix": "match",
|
"prefix": "match",
|
||||||
"body": [
|
"body": [
|
||||||
"match (${1:result}) {",
|
"match (${1:result}) {",
|
||||||
" ${2:Result}::${3:Success}(${4:value}) -> {",
|
" ${2:Result}.${3:Success}(${4:value}) -> {",
|
||||||
" $5",
|
" $5",
|
||||||
" }",
|
" }",
|
||||||
" ${2:Result}::${6:Failure}(${7:reason}) -> {",
|
" ${2:Result}.${6:Failure}(${7:reason}) -> {",
|
||||||
" $8",
|
" $8",
|
||||||
" }",
|
" }",
|
||||||
" ${2:Result}::${9:Pending} -> {",
|
" ${2:Result}.${9:Pending} -> {",
|
||||||
" $0",
|
" $0",
|
||||||
" }",
|
" }",
|
||||||
"}"
|
"}"
|
||||||
|
|
@ -84,8 +84,8 @@
|
||||||
"prefix": "matchvalue",
|
"prefix": "matchvalue",
|
||||||
"body": [
|
"body": [
|
||||||
"val ${1:value} = match (${2:result}) {",
|
"val ${1:value} = match (${2:result}) {",
|
||||||
" ${3:Result}::${4:Success}(${5:item}) -> ${5:item}",
|
" ${3:Result}.${4:Success}(${5:item}) -> ${5:item}",
|
||||||
" ${3:Result}::${6:Failure}(${7:reason}) -> ${7:reason}",
|
" ${3:Result}.${6:Failure}(${7:reason}) -> ${7:reason}",
|
||||||
" $0",
|
" $0",
|
||||||
"}"
|
"}"
|
||||||
],
|
],
|
||||||
|
|
@ -111,7 +111,7 @@
|
||||||
"body": [
|
"body": [
|
||||||
"fun ${1:name}(${2}): Result<${3:Value}, Error> {",
|
"fun ${1:name}(${2}): Result<${3:Value}, Error> {",
|
||||||
" val ${4:value} = ${5:operation}()?",
|
" val ${4:value} = ${5:operation}()?",
|
||||||
" return Result::Ok(${4:value})",
|
" return Result.Ok(${4:value})",
|
||||||
"}"
|
"}"
|
||||||
],
|
],
|
||||||
"description": "Function with Rust-style Result propagation"
|
"description": "Function with Rust-style Result propagation"
|
||||||
|
|
@ -120,8 +120,8 @@
|
||||||
"prefix": "resultmatch",
|
"prefix": "resultmatch",
|
||||||
"body": [
|
"body": [
|
||||||
"match (${1:result}) {",
|
"match (${1:result}) {",
|
||||||
" Result::Ok(${2:value}) -> { $3 }",
|
" Result.Ok(${2:value}) -> { $3 }",
|
||||||
" Result::Err(${4:error}) -> { $0 }",
|
" Result.Err(${4:error}) -> { $0 }",
|
||||||
"}"
|
"}"
|
||||||
],
|
],
|
||||||
"description": "Match a Result value"
|
"description": "Match a Result value"
|
||||||
|
|
|
||||||
|
|
@ -356,10 +356,6 @@
|
||||||
"name": "keyword.operator.arrow.gotlin",
|
"name": "keyword.operator.arrow.gotlin",
|
||||||
"match": "->"
|
"match": "->"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "punctuation.accessor.enum.gotlin",
|
|
||||||
"match": "::"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "keyword.operator.arithmetic.gotlin",
|
"name": "keyword.operator.arithmetic.gotlin",
|
||||||
"match": "[+\\-*/%<>]"
|
"match": "[+\\-*/%<>]"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue