github.com/goplus/gox@v1.14.13-0.20240308130321-6ff7f61cfae8/stmt.go (about) 1 /* 2 Copyright 2021 The GoPlus Authors (goplus.org) 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 http://www.apache.org/licenses/LICENSE-2.0 7 Unless required by applicable law or agreed to in writing, software 8 distributed under the License is distributed on an "AS IS" BASIS, 9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 See the License for the specific language governing permissions and 11 limitations under the License. 12 */ 13 14 package gox 15 16 import ( 17 "go/ast" 18 "go/token" 19 "go/types" 20 "log" 21 22 "github.com/goplus/gox/internal" 23 ) 24 25 type controlFlow interface { 26 Then(cb *CodeBuilder, src ...ast.Node) 27 } 28 29 // ---------------------------------------------------------------------------- 30 // 31 // block 32 // 33 // ... 34 // 35 // end 36 type blockStmt struct { 37 old codeBlockCtx 38 } 39 40 func (p *blockStmt) End(cb *CodeBuilder, src ast.Node) { 41 stmts, flows := cb.endBlockStmt(&p.old) 42 cb.current.flows |= flows 43 cb.emitStmt(&ast.BlockStmt{List: stmts}) 44 } 45 46 // ---------------------------------------------------------------------------- 47 // 48 // vblock 49 // 50 // ... 51 // 52 // end 53 type vblockStmt struct { 54 old vblockCtx 55 } 56 57 func (p *vblockStmt) End(cb *CodeBuilder, src ast.Node) { 58 cb.endVBlockStmt(&p.old) 59 } 60 61 // ---------------------------------------------------------------------------- 62 // 63 // if init; cond then 64 // 65 // ... 66 // 67 // else 68 // 69 // ... 70 // 71 // end 72 type ifStmt struct { 73 init ast.Stmt 74 cond ast.Expr 75 body *ast.BlockStmt 76 old codeBlockCtx 77 old2 codeBlockCtx 78 } 79 80 func (p *ifStmt) Then(cb *CodeBuilder, src ...ast.Node) { 81 cond := cb.stk.Pop() 82 if !types.AssignableTo(cond.Type, types.Typ[types.Bool]) { 83 cb.panicCodeError(getPos(src), "non-boolean condition in if statement") 84 } 85 p.cond = cond.Val 86 switch stmts := cb.clearBlockStmt(); len(stmts) { 87 case 0: 88 // nothing to do 89 case 1: 90 p.init = stmts[0] 91 default: 92 panic("if statement has too many init statements") 93 } 94 cb.startBlockStmt(p, src, "if body", &p.old2) 95 } 96 97 func (p *ifStmt) Else(cb *CodeBuilder, src ...ast.Node) { 98 if p.body != nil { 99 panic("else statement already exists") 100 } 101 102 stmts, flows := cb.endBlockStmt(&p.old2) 103 cb.current.flows |= flows 104 105 p.body = &ast.BlockStmt{List: stmts} 106 cb.startBlockStmt(p, src, "else body", &p.old2) 107 } 108 109 func (p *ifStmt) End(cb *CodeBuilder, src ast.Node) { 110 stmts, flows := cb.endBlockStmt(&p.old2) 111 cb.current.flows |= flows 112 113 var blockStmt = &ast.BlockStmt{List: stmts} 114 var el ast.Stmt 115 if p.body != nil { // if..else 116 el = blockStmt 117 if len(stmts) == 1 { // if..else if 118 if stmt, ok := stmts[0].(*ast.IfStmt); ok { 119 el = stmt 120 } 121 } 122 } else { // if without else 123 p.body = blockStmt 124 } 125 cb.endBlockStmt(&p.old) 126 cb.emitStmt(&ast.IfStmt{Init: p.init, Cond: p.cond, Body: p.body, Else: el}) 127 } 128 129 // ---------------------------------------------------------------------------- 130 // 131 // switch init; tag then 132 // 133 // case expr1, expr2, ..., exprN then 134 // ... 135 // end 136 // 137 // case expr1, expr2, ..., exprM then 138 // ... 139 // end 140 // 141 // defaultThen 142 // ... 143 // end 144 // 145 // end 146 type switchStmt struct { 147 init ast.Stmt 148 tag *internal.Elem 149 old codeBlockCtx 150 } 151 152 func (p *switchStmt) Then(cb *CodeBuilder, src ...ast.Node) { 153 if cb.stk.Len() == cb.current.base { 154 panic("use None() for empty switch tag") 155 } 156 p.tag = cb.stk.Pop() 157 switch stmts := cb.clearBlockStmt(); len(stmts) { 158 case 0: 159 // nothing to do 160 case 1: 161 p.init = stmts[0] 162 default: 163 panic("switch statement has too many init statements") 164 } 165 } 166 167 func (p *switchStmt) Case(cb *CodeBuilder, src ...ast.Node) { 168 stmt := &caseStmt{tag: p.tag} 169 cb.startBlockStmt(stmt, src, "case statement", &stmt.old) 170 } 171 172 func (p *switchStmt) End(cb *CodeBuilder, src ast.Node) { 173 if p.tag == nil { 174 return 175 } 176 stmts, flows := cb.endBlockStmt(&p.old) 177 cb.current.flows |= (flows &^ flowFlagBreak) 178 179 body := &ast.BlockStmt{List: stmts} 180 cb.emitStmt(&ast.SwitchStmt{Init: p.init, Tag: checkParenExpr(p.tag.Val), Body: body}) 181 } 182 183 type caseStmt struct { 184 tag *internal.Elem 185 list []ast.Expr 186 old codeBlockCtx 187 } 188 189 func (p *caseStmt) Then(cb *CodeBuilder, src ...ast.Node) { 190 n := cb.stk.Len() - cb.current.base 191 if n > 0 { 192 p.list = make([]ast.Expr, n) 193 for i, arg := range cb.stk.GetArgs(n) { 194 if p.tag.Val != nil { // switch tag {...} 195 if !ComparableTo(cb.pkg, arg, p.tag) { 196 src, pos := cb.loadExpr(arg.Src) 197 cb.panicCodeErrorf( 198 pos, "cannot use %s (type %v) as type %v", src, arg.Type, types.Default(p.tag.Type)) 199 } 200 } else { // switch {...} 201 if !types.AssignableTo(arg.Type, types.Typ[types.Bool]) && arg.Type != TyEmptyInterface { 202 src, pos := cb.loadExpr(arg.Src) 203 cb.panicCodeErrorf(pos, "cannot use %s (type %v) as type bool", src, arg.Type) 204 } 205 } 206 p.list[i] = arg.Val 207 } 208 cb.stk.PopN(n) 209 } 210 } 211 212 func (p *caseStmt) Fallthrough(cb *CodeBuilder) { 213 cb.emitStmt(&ast.BranchStmt{Tok: token.FALLTHROUGH}) 214 } 215 216 func (p *caseStmt) End(cb *CodeBuilder, src ast.Node) { 217 body, flows := cb.endBlockStmt(&p.old) 218 cb.current.flows |= flows 219 cb.emitStmt(&ast.CaseClause{List: p.list, Body: body}) 220 } 221 222 // ---------------------------------------------------------------------------- 223 // 224 // select 225 // commCase commStmt1 then 226 // 227 // ... 228 // end 229 // 230 // commCase commStmt2 then 231 // 232 // ... 233 // end 234 // 235 // commDefaultThen 236 // 237 // ... 238 // end 239 // 240 // end 241 type selectStmt struct { 242 old codeBlockCtx 243 } 244 245 func (p *selectStmt) CommCase(cb *CodeBuilder, src ...ast.Node) { 246 stmt := &commCase{} 247 cb.startBlockStmt(stmt, src, "comm case statement", &stmt.old) 248 } 249 250 func (p *selectStmt) End(cb *CodeBuilder, src ast.Node) { 251 stmts, flows := cb.endBlockStmt(&p.old) 252 cb.current.flows |= (flows &^ flowFlagBreak) 253 cb.emitStmt(&ast.SelectStmt{Body: &ast.BlockStmt{List: stmts}}) 254 } 255 256 type commCase struct { 257 old codeBlockCtx 258 comm ast.Stmt 259 } 260 261 func (p *commCase) Then(cb *CodeBuilder, src ...ast.Node) { 262 switch len(cb.current.stmts) { 263 case 1: 264 p.comm = cb.popStmt() 265 case 0: 266 default: 267 panic("multi commStmt in comm clause?") 268 } 269 } 270 271 func (p *commCase) End(cb *CodeBuilder, src ast.Node) { 272 body, flows := cb.endBlockStmt(&p.old) 273 cb.current.flows |= flows 274 cb.emitStmt(&ast.CommClause{Comm: p.comm, Body: body}) 275 } 276 277 // ---------------------------------------------------------------------------- 278 // 279 // typeSwitch(name) init; expr typeAssertThen 280 // typeCase type1, type2, ... typeN then 281 // 282 // ... 283 // end 284 // 285 // typeCase type1, type2, ... typeM then 286 // 287 // ... 288 // end 289 // 290 // typeDefaultThen 291 // 292 // ... 293 // end 294 // 295 // end 296 type typeSwitchStmt struct { 297 init ast.Stmt 298 name string 299 x ast.Expr 300 xSrc ast.Node 301 xType *types.Interface 302 old codeBlockCtx 303 } 304 305 func (p *typeSwitchStmt) TypeAssertThen(cb *CodeBuilder) { 306 switch stmts := cb.clearBlockStmt(); len(stmts) { 307 case 0: 308 // nothing to do 309 case 1: 310 p.init = stmts[0] 311 default: 312 panic("TODO: type switch statement has too many init statements") 313 } 314 x := cb.stk.Pop() 315 xType, ok := cb.checkInterface(x.Type) 316 if !ok { 317 panic("TODO: can't type assert on non interface expr") 318 } 319 p.x, p.xSrc, p.xType = x.Val, x.Src, xType 320 } 321 322 func (p *typeSwitchStmt) TypeCase(cb *CodeBuilder, src ...ast.Node) { 323 stmt := &typeCaseStmt{pss: p} 324 cb.startBlockStmt(stmt, src, "type case statement", &stmt.old) 325 } 326 327 func (p *typeSwitchStmt) End(cb *CodeBuilder, src ast.Node) { 328 stmts, flows := cb.endBlockStmt(&p.old) 329 cb.current.flows |= (flows &^ flowFlagBreak) 330 331 body := &ast.BlockStmt{List: stmts} 332 var assign ast.Stmt 333 x := &ast.TypeAssertExpr{X: p.x} 334 if p.name != "" { 335 assign = &ast.AssignStmt{ 336 Tok: token.DEFINE, 337 Lhs: []ast.Expr{ident(p.name)}, 338 Rhs: []ast.Expr{x}, 339 } 340 } else { 341 assign = &ast.ExprStmt{X: x} 342 } 343 cb.emitStmt(&ast.TypeSwitchStmt{Init: p.init, Assign: assign, Body: body}) 344 } 345 346 type typeCaseStmt struct { 347 pss *typeSwitchStmt 348 list []ast.Expr 349 old codeBlockCtx 350 } 351 352 func (p *typeCaseStmt) Then(cb *CodeBuilder, src ...ast.Node) { 353 var typ types.Type 354 pss := p.pss 355 n := cb.stk.Len() - cb.current.base 356 if n > 0 { 357 p.list = make([]ast.Expr, n) 358 args := cb.stk.GetArgs(n) 359 for i, arg := range args { 360 typ = arg.Type 361 if tt, ok := typ.(*TypeType); ok { 362 typ = tt.Type() 363 if missing := cb.missingMethod(typ, pss.xType); missing != "" { 364 xsrc, _ := cb.loadExpr(pss.xSrc) 365 pos := getSrcPos(arg.Src) 366 cb.panicCodeErrorf( 367 pos, "impossible type switch case: %s (type %v) cannot have dynamic type %v (missing %s method)", 368 xsrc, pss.xType, typ, missing) 369 } 370 } else if typ != types.Typ[types.UntypedNil] { 371 src, pos := cb.loadExpr(arg.Src) 372 cb.panicCodeErrorf(pos, "%s (type %v) is not a type", src, typ) 373 } 374 p.list[i] = arg.Val 375 } 376 cb.stk.PopN(n) 377 } 378 if pss.name != "" { 379 if n != 1 { // default, or case with multi expr 380 typ = pss.xType 381 } 382 name := types.NewParam(token.NoPos, cb.pkg.Types, pss.name, typ) 383 cb.current.scope.Insert(name) 384 } 385 } 386 387 func (p *typeCaseStmt) End(cb *CodeBuilder, src ast.Node) { 388 body, flows := cb.endBlockStmt(&p.old) 389 cb.current.flows |= flows 390 cb.emitStmt(&ast.CaseClause{List: p.list, Body: body}) 391 } 392 393 // ---------------------------------------------------------------------------- 394 395 type loopBodyHandler struct { 396 handle func(body *ast.BlockStmt, kind int) 397 } 398 399 func (p *loopBodyHandler) SetBodyHandler(handle func(body *ast.BlockStmt, kind int)) { 400 p.handle = handle 401 } 402 403 func (p *loopBodyHandler) handleFor(body *ast.BlockStmt, kind int) *ast.BlockStmt { 404 if p.handle != nil { 405 p.handle(body, kind) 406 } 407 return body 408 } 409 410 func InsertStmtFront(body *ast.BlockStmt, stmt ast.Stmt) { 411 list := append(body.List, nil) 412 copy(list[1:], list) 413 list[0] = stmt 414 body.List = list 415 } 416 417 // ---------------------------------------------------------------------------- 418 // 419 // for init; cond then 420 // 421 // body 422 // post 423 // 424 // end 425 type forStmt struct { 426 init ast.Stmt 427 cond ast.Expr 428 body *ast.BlockStmt 429 old codeBlockCtx 430 old2 codeBlockCtx 431 loopBodyHandler 432 } 433 434 func (p *forStmt) Then(cb *CodeBuilder, src ...ast.Node) { 435 cond := cb.stk.Pop() 436 if cond.Val != nil { 437 if !types.AssignableTo(cond.Type, types.Typ[types.Bool]) { 438 panic("TODO: for statement condition is not a boolean expr") 439 } 440 p.cond = cond.Val 441 } 442 switch stmts := cb.clearBlockStmt(); len(stmts) { 443 case 0: 444 // nothing to do 445 case 1: 446 p.init = stmts[0] 447 default: 448 panic("TODO: for condition has too many init statements") 449 } 450 cb.startBlockStmt(p, src, "for body", &p.old2) 451 } 452 453 func (p *forStmt) Post(cb *CodeBuilder) { 454 stmts, flows := cb.endBlockStmt(&p.old2) 455 cb.current.flows |= (flows &^ (flowFlagBreak | flowFlagContinue)) 456 p.body = &ast.BlockStmt{List: stmts} 457 } 458 459 func (p *forStmt) End(cb *CodeBuilder, src ast.Node) { 460 var post ast.Stmt 461 if p.body != nil { // has post stmt 462 stmts, _ := cb.endBlockStmt(&p.old) 463 if len(stmts) != 1 { 464 panic("TODO: too many post statements") 465 } 466 post = stmts[0] 467 } else { // no post 468 stmts, flows := cb.endBlockStmt(&p.old2) 469 cb.current.flows |= (flows &^ (flowFlagBreak | flowFlagContinue)) 470 p.body = &ast.BlockStmt{List: stmts} 471 cb.endBlockStmt(&p.old) 472 } 473 cb.emitStmt(&ast.ForStmt{ 474 Init: p.init, Cond: p.cond, Post: post, Body: p.handleFor(p.body, 0), 475 }) 476 } 477 478 // ---------------------------------------------------------------------------- 479 // 480 // forRange names... exprX rangeAssignThen 481 // 482 // ... 483 // 484 // end 485 // 486 // forRange exprKey exprVal exprX rangeAssignThen 487 // 488 // ... 489 // 490 // end 491 type forRangeStmt struct { 492 names []string 493 stmt *ast.RangeStmt 494 x *internal.Elem 495 old codeBlockCtx 496 kvt []types.Type 497 udt int // 0: non-udt, 2: (elem,ok), 3: (key,elem,ok) 498 loopBodyHandler 499 } 500 501 func (p *forRangeStmt) RangeAssignThen(cb *CodeBuilder, pos token.Pos) { 502 if names := p.names; names != nil { // for k, v := range XXX { 503 var val ast.Expr 504 switch len(names) { 505 case 1: 506 case 2: 507 val = ident(names[1]) 508 default: 509 cb.panicCodeError(pos, "too many variables in range") 510 } 511 x := cb.stk.Pop() 512 pkg, scope := cb.pkg, cb.current.scope 513 typs := p.getKeyValTypes(cb, x.Type) 514 if typs == nil { 515 src, _ := cb.loadExpr(x.Src) 516 cb.panicCodeErrorf(pos, "cannot range over %v (type %v)", src, x.Type) 517 } 518 if typs[1] == nil { // chan 519 if names[0] == "_" && len(names) > 1 { 520 names[0], val = names[1], nil 521 names = names[:1] 522 } 523 } 524 for i, name := range names { 525 if name == "_" { 526 continue 527 } 528 if scope.Insert(types.NewVar(token.NoPos, pkg.Types, name, typs[i])) != nil { 529 log.Panicln("TODO: variable already defined -", name) 530 } 531 } 532 if p.udt != 0 { 533 p.x = x 534 } 535 p.stmt = &ast.RangeStmt{ 536 Key: ident(names[0]), 537 Value: val, 538 Tok: token.DEFINE, 539 X: x.Val, 540 } 541 } else { // for k, v = range XXX { 542 var key, val, x internal.Elem 543 n := cb.stk.Len() - cb.current.base 544 args := cb.stk.GetArgs(n) 545 switch n { 546 case 1: 547 x = *args[0] 548 case 2: 549 key, x = *args[0], *args[1] 550 case 3: 551 key, val, x = *args[0], *args[1], *args[2] 552 default: 553 cb.panicCodeError(pos, "too many variables in range") 554 } 555 cb.stk.PopN(n) 556 typs := p.getKeyValTypes(cb, x.Type) 557 if typs == nil { 558 src, _ := cb.loadExpr(x.Src) 559 cb.panicCodeErrorf(pos, "cannot range over %v (type %v)", src, x.Type) 560 } 561 if p.udt != 0 { 562 p.x = &x 563 } 564 p.stmt = &ast.RangeStmt{ 565 Key: key.Val, 566 Value: val.Val, 567 X: x.Val, 568 } 569 if n > 1 { 570 p.stmt.Tok = token.ASSIGN 571 checkAssign(cb.pkg, &key, typs[0], "range") 572 if val.Val != nil { 573 checkAssign(cb.pkg, &val, typs[1], "range") 574 } 575 } 576 } 577 p.stmt.For = pos 578 } 579 580 func (p *forRangeStmt) getKeyValTypes(cb *CodeBuilder, typ types.Type) []types.Type { 581 retry: 582 switch t := typ.(type) { 583 case *types.Slice: 584 return []types.Type{types.Typ[types.Int], t.Elem()} 585 case *types.Map: 586 return []types.Type{t.Key(), t.Elem()} 587 case *types.Array: 588 return []types.Type{types.Typ[types.Int], t.Elem()} 589 case *types.Pointer: 590 switch e := t.Elem().(type) { 591 case *types.Array: 592 return []types.Type{types.Typ[types.Int], e.Elem()} 593 case *types.Named: 594 if kv, ok := p.checkUdt(cb, e); ok { 595 return kv 596 } 597 } 598 case *types.Chan: 599 return []types.Type{t.Elem(), nil} 600 case *types.Basic: 601 if (t.Info() & types.IsString) != 0 { 602 return []types.Type{types.Typ[types.Int], types.Typ[types.Rune]} 603 } 604 case *types.Named: 605 if kv, ok := p.checkUdt(cb, t); ok { 606 return kv 607 } 608 typ = cb.getUnderlying(t) 609 goto retry 610 } 611 return nil 612 } 613 614 func (p *forRangeStmt) checkUdt(cb *CodeBuilder, o *types.Named) ([]types.Type, bool) { 615 if sig := findMethodType(cb, o, nameGopEnum); sig != nil { 616 enumRet := sig.Results() 617 params := sig.Params() 618 switch params.Len() { 619 case 0: 620 // iter := obj.Gop_Enum() 621 // key, val, ok := iter.Next() 622 case 1: 623 // obj.Gop_Enum(func(key K, val V) { ... }) 624 if enumRet.Len() != 0 { 625 return nil, false 626 } 627 typ := params.At(0).Type() 628 if t, ok := typ.(*types.Signature); ok && t.Results().Len() == 0 { 629 kv := t.Params() 630 n := kv.Len() 631 if n > 0 { 632 p.kvt = []types.Type{kv.At(0).Type(), nil} 633 if n > 1 { 634 n = 2 635 p.kvt[1] = kv.At(1).Type() 636 } 637 p.udt = -n 638 return p.kvt, true 639 } 640 } 641 fallthrough 642 default: 643 return nil, false 644 } 645 if enumRet.Len() == 1 { 646 typ := enumRet.At(0).Type() 647 if t, ok := typ.(*types.Pointer); ok { 648 typ = t.Elem() 649 } 650 if it, ok := typ.(*types.Named); ok { 651 if next := findMethodType(cb, it, "Next"); next != nil { 652 ret := next.Results() 653 typs := make([]types.Type, 2) 654 n := ret.Len() 655 switch n { 656 case 2: // elem, ok 657 typs[0] = ret.At(0).Type() 658 case 3: // key, elem, ok 659 typs[0], typs[1] = ret.At(0).Type(), ret.At(1).Type() 660 default: 661 return nil, false 662 } 663 if ret.At(n-1).Type() == types.Typ[types.Bool] { 664 p.udt = n 665 return typs, true 666 } 667 } 668 } 669 } 670 } 671 return nil, false 672 } 673 674 func findMethodType(cb *CodeBuilder, o *types.Named, name string) mthdSignature { 675 for i, n := 0, o.NumMethods(); i < n; i++ { 676 method := o.Method(i) 677 if method.Name() == name { 678 return method.Type().(*types.Signature) 679 } 680 } 681 if bti := cb.getBuiltinTI(o); bti != nil { 682 return bti.lookupByName(name) 683 } 684 return nil 685 } 686 687 const ( 688 cantUseFlows = "can't use return/continue/break/goto in for range of udt.Gop_Enum(callback)" 689 ) 690 691 func (p *forRangeStmt) End(cb *CodeBuilder, src ast.Node) { 692 if p.stmt == nil { 693 return 694 } 695 stmts, flows := cb.endBlockStmt(&p.old) 696 cb.current.flows |= (flows &^ (flowFlagBreak | flowFlagContinue)) 697 if n := p.udt; n == 0 { 698 p.stmt.Body = p.handleFor(&ast.BlockStmt{List: stmts}, 1) 699 cb.emitStmt(p.stmt) 700 } else if n > 0 { 701 cb.stk.Push(p.x) 702 cb.MemberVal(nameGopEnum).Call(0) 703 callEnum := cb.stk.Pop().Val 704 /* 705 for _gop_it := X.Gop_Enum();; { 706 var _gop_ok bool 707 k, v, _gop_ok = _gop_it.Next() 708 if !_gop_ok { 709 break 710 } 711 ... 712 } 713 */ 714 lhs := make([]ast.Expr, n) 715 lhs[0] = p.stmt.Key 716 lhs[1] = p.stmt.Value 717 lhs[n-1] = identGopOk 718 if lhs[0] == nil { // bugfix: for range udt { ... } 719 lhs[0] = underscore 720 if p.stmt.Tok == token.ILLEGAL { 721 p.stmt.Tok = token.ASSIGN 722 } 723 } else { 724 // for _ = range udt { ... } 725 if ki, ok := lhs[0].(*ast.Ident); ok && ki.Name == "_" { 726 if n == 2 { 727 p.stmt.Tok = token.ASSIGN 728 } else { 729 // for _ , _ = range udt { ... } 730 if vi, ok := lhs[1].(*ast.Ident); ok && vi.Name == "_" { 731 p.stmt.Tok = token.ASSIGN 732 } 733 } 734 } 735 } 736 body := make([]ast.Stmt, len(stmts)+3) 737 body[0] = stmtGopOkDecl 738 body[1] = &ast.AssignStmt{Lhs: lhs, Tok: p.stmt.Tok, Rhs: exprIterNext} 739 body[2] = stmtBreakIfNotGopOk 740 copy(body[3:], stmts) 741 stmt := &ast.ForStmt{ 742 Init: &ast.AssignStmt{ 743 Lhs: []ast.Expr{identGopIt}, 744 Tok: token.DEFINE, 745 Rhs: []ast.Expr{callEnum}, 746 }, 747 Body: p.handleFor(&ast.BlockStmt{List: body}, 2), 748 } 749 cb.emitStmt(stmt) 750 } else { 751 /* 752 X.Gop_Enum(func(k K, v V) { 753 ... 754 }) 755 */ 756 if flows != 0 { 757 cb.panicCodeError(p.stmt.For, cantUseFlows) 758 } 759 n = -n 760 def := p.stmt.Tok == token.DEFINE 761 args := make([]*ast.Field, n) 762 if def { 763 args[0] = &ast.Field{ 764 Names: []*ast.Ident{p.stmt.Key.(*ast.Ident)}, 765 Type: toType(cb.pkg, p.kvt[0]), 766 } 767 if n > 1 { 768 args[1] = &ast.Field{ 769 Names: []*ast.Ident{p.stmt.Value.(*ast.Ident)}, 770 Type: toType(cb.pkg, p.kvt[1]), 771 } 772 } 773 } else { 774 panic("TODO: for range udt assign") 775 } 776 stmt := &ast.ExprStmt{ 777 X: &ast.CallExpr{ 778 Fun: &ast.SelectorExpr{X: p.stmt.X, Sel: identGopEnum}, 779 Args: []ast.Expr{ 780 &ast.FuncLit{ 781 Type: &ast.FuncType{Params: &ast.FieldList{List: args}}, 782 Body: p.handleFor(&ast.BlockStmt{List: stmts}, -1), 783 }, 784 }, 785 }, 786 } 787 cb.emitStmt(stmt) 788 } 789 } 790 791 var ( 792 nameGopEnum = "Gop_Enum" 793 identGopOk = ident("_gop_ok") 794 identGopIt = ident("_gop_it") 795 identGopEnum = ident(nameGopEnum) 796 ) 797 798 var ( 799 stmtGopOkDecl = &ast.DeclStmt{ 800 Decl: &ast.GenDecl{ 801 Tok: token.VAR, 802 Specs: []ast.Spec{ 803 &ast.ValueSpec{Names: []*ast.Ident{identGopOk}, Type: ident("bool")}, 804 }, 805 }, 806 } 807 stmtBreakIfNotGopOk = &ast.IfStmt{ 808 Cond: &ast.UnaryExpr{Op: token.NOT, X: identGopOk}, 809 Body: &ast.BlockStmt{List: []ast.Stmt{&ast.BranchStmt{Tok: token.BREAK}}}, 810 } 811 exprIterNext = []ast.Expr{ 812 &ast.CallExpr{ 813 Fun: &ast.SelectorExpr{X: identGopIt, Sel: ident("Next")}, 814 }, 815 } 816 ) 817 818 // ----------------------------------------------------------------------------