github.com/elves/elvish@v0.15.0/pkg/eval/builtin_special.go (about) 1 package eval 2 3 // Builtin special forms. Special forms behave mostly like ordinary commands - 4 // they are valid commands syntactically, and can take part in pipelines - but 5 // they have special rules for the evaluation of their arguments and can affect 6 // the compilation phase (whereas ordinary commands can only affect the 7 // evaluation phase). 8 // 9 // For example, the "and" special form evaluates its arguments from left to 10 // right, and stops as soon as one booleanly false value is obtained: the 11 // command "and $false (fail haha)" does not produce an exception. 12 // 13 // As another example, the "del" special form removes a variable, affecting the 14 // compiler. 15 // 16 // Flow control structures are also implemented as special forms in elvish, with 17 // closures functioning as code blocks. 18 19 import ( 20 "os" 21 "path/filepath" 22 "strings" 23 24 "github.com/elves/elvish/pkg/diag" 25 "github.com/elves/elvish/pkg/eval/mods/bundled" 26 "github.com/elves/elvish/pkg/eval/vals" 27 "github.com/elves/elvish/pkg/eval/vars" 28 "github.com/elves/elvish/pkg/parse" 29 ) 30 31 type compileBuiltin func(*compiler, *parse.Form) effectOp 32 33 var builtinSpecials map[string]compileBuiltin 34 35 // IsBuiltinSpecial is the set of all names of builtin special forms. It is 36 // intended for external consumption, e.g. the syntax highlighter. 37 var IsBuiltinSpecial = map[string]bool{} 38 39 type noSuchModule struct{ spec string } 40 41 func (err noSuchModule) Error() string { return "no such module: " + err.spec } 42 43 func init() { 44 // Needed to avoid initialization loop 45 builtinSpecials = map[string]compileBuiltin{ 46 "var": compileVar, 47 "set": compileSet, 48 "del": compileDel, 49 "fn": compileFn, 50 51 "use": compileUse, 52 53 "and": compileAnd, 54 "or": compileOr, 55 56 "if": compileIf, 57 "while": compileWhile, 58 "for": compileFor, 59 "try": compileTry, 60 } 61 for name := range builtinSpecials { 62 IsBuiltinSpecial[name] = true 63 } 64 } 65 66 // VarForm = 'var' { VariablePrimary } [ '=' { Compound } ] 67 func compileVar(cp *compiler, fn *parse.Form) effectOp { 68 lhs := lvaluesGroup{rest: -1} 69 for i, cn := range fn.Args { 70 if parse.SourceText(cn) == "=" { 71 var rhs valuesOp 72 if i == len(fn.Args)-1 { 73 rhs = nopValuesOp{diag.PointRanging(fn.Range().To)} 74 } else { 75 rhs = seqValuesOp{ 76 diag.MixedRanging(fn.Args[i+1], fn.Args[len(fn.Args)-1]), 77 cp.compoundOps(fn.Args[i+1:])} 78 } 79 return &assignOp{fn.Range(), lhs, rhs} 80 } 81 if len(cn.Indexings) != 1 { 82 cp.errorpf(cn, "variable name must be a single string literal") 83 } 84 if len(cn.Indexings[0].Indicies) > 0 { 85 cp.errorpf(cn, "variable name must not have indicies") 86 } 87 pn := cn.Indexings[0].Head 88 if !parse.ValidLHSVariable(pn, true) { 89 cp.errorpf(cn, "invalid variable name") 90 } 91 92 name := pn.Value 93 if !IsUnqualified(name) { 94 cp.errorpf(cn, "variable declared in var must be unqualified") 95 } 96 sigil, name := SplitSigil(name) 97 if sigil == "@" { 98 if lhs.rest != -1 { 99 cp.errorpf(cn, "multiple variable names with @ not allowed") 100 } 101 lhs.rest = i 102 } 103 slotIndex := cp.thisScope().add(name) 104 lhs.lvalues = append(lhs.lvalues, 105 lvalue{cn.Range(), &varRef{localScope, slotIndex, nil}, nil, nil}) 106 } 107 // If there is no assignment, there is no work to be done at eval-time. 108 return nopOp{} 109 } 110 111 // IsUnqualified returns whether name is an unqualified variable name. 112 func IsUnqualified(name string) bool { 113 i := strings.IndexByte(name, ':') 114 return i == -1 || i == len(name)-1 115 } 116 117 // SetForm = 'set' { LHS } '=' { Compound } 118 func compileSet(cp *compiler, fn *parse.Form) effectOp { 119 eq := -1 120 for i, cn := range fn.Args { 121 if parse.SourceText(cn) == "=" { 122 eq = i 123 break 124 } 125 } 126 if eq == -1 { 127 cp.errorpf(diag.PointRanging(fn.Range().To), "need = and right-hand-side") 128 } 129 lhs := cp.parseCompoundLValues(fn.Args[:eq]) 130 var rhs valuesOp 131 if eq == len(fn.Args)-1 { 132 rhs = nopValuesOp{diag.PointRanging(fn.Range().To)} 133 } else { 134 rhs = seqValuesOp{ 135 diag.MixedRanging(fn.Args[eq+1], fn.Args[len(fn.Args)-1]), 136 cp.compoundOps(fn.Args[eq+1:])} 137 } 138 return &assignOp{fn.Range(), lhs, rhs} 139 140 } 141 142 const delArgMsg = "arguments to del must be variable or variable elements" 143 144 // DelForm = 'del' { LHS } 145 func compileDel(cp *compiler, fn *parse.Form) effectOp { 146 var ops []effectOp 147 for _, cn := range fn.Args { 148 if len(cn.Indexings) != 1 { 149 cp.errorpf(cn, delArgMsg) 150 continue 151 } 152 head, indices := cn.Indexings[0].Head, cn.Indexings[0].Indicies 153 if head.Type == parse.Variable { 154 cp.errorpf(cn, "arguments to del must drop $") 155 } else if !parse.ValidLHSVariable(head, false) { 156 cp.errorpf(cn, delArgMsg) 157 } 158 159 qname := head.Value 160 var f effectOp 161 ref := resolveVarRef(cp, qname, nil) 162 if ref == nil { 163 cp.errorpf(cn, "no variable $%s", head.Value) 164 continue 165 } 166 if len(indices) == 0 { 167 if ref.scope == envScope { 168 f = delEnvVarOp{fn.Range(), ref.subNames[0]} 169 } else if ref.scope == localScope && len(ref.subNames) == 0 { 170 f = delLocalVarOp{ref.index} 171 cp.thisScope().deleted[ref.index] = true 172 } else { 173 cp.errorpf(cn, "only variables in local: or E: can be deleted") 174 continue 175 } 176 } else { 177 f = newDelElementOp(ref, head.Range().From, head.Range().To, cp.arrayOps(indices)) 178 } 179 ops = append(ops, f) 180 } 181 return seqOp{ops} 182 } 183 184 type delLocalVarOp struct{ index int } 185 186 func (op delLocalVarOp) exec(fm *Frame) Exception { 187 fm.local.slots[op.index] = nil 188 return nil 189 } 190 191 type delEnvVarOp struct { 192 diag.Ranging 193 name string 194 } 195 196 func (op delEnvVarOp) exec(fm *Frame) Exception { 197 return fm.errorp(op, os.Unsetenv(op.name)) 198 } 199 200 func newDelElementOp(ref *varRef, begin, headEnd int, indexOps []valuesOp) effectOp { 201 ends := make([]int, len(indexOps)+1) 202 ends[0] = headEnd 203 for i, op := range indexOps { 204 ends[i+1] = op.Range().To 205 } 206 return &delElemOp{ref, indexOps, begin, ends} 207 } 208 209 type delElemOp struct { 210 ref *varRef 211 indexOps []valuesOp 212 begin int 213 ends []int 214 } 215 216 func (op *delElemOp) Range() diag.Ranging { 217 return diag.Ranging{From: op.begin, To: op.ends[0]} 218 } 219 220 func (op *delElemOp) exec(fm *Frame) Exception { 221 var indices []interface{} 222 for _, indexOp := range op.indexOps { 223 indexValues, exc := indexOp.exec(fm) 224 if exc != nil { 225 return exc 226 } 227 if len(indexValues) != 1 { 228 return fm.errorpf(indexOp, "index must evaluate to a single value in argument to del") 229 } 230 indices = append(indices, indexValues[0]) 231 } 232 err := vars.DelElement(deref(fm, op.ref), indices) 233 if err != nil { 234 if level := vars.ElementErrorLevel(err); level >= 0 { 235 return fm.errorp(diag.Ranging{From: op.begin, To: op.ends[level]}, err) 236 } 237 return fm.errorp(op, err) 238 } 239 return nil 240 } 241 242 // FnForm = 'fn' StringPrimary LambdaPrimary 243 // 244 // fn f []{foobar} is a shorthand for set '&'f = []{foobar}. 245 func compileFn(cp *compiler, fn *parse.Form) effectOp { 246 args := cp.walkArgs(fn) 247 nameNode := args.next() 248 name := mustString(cp, nameNode, "must be a literal string") 249 bodyNode := args.nextMustLambda() 250 args.mustEnd() 251 252 // Define the variable before compiling the body, so that the body may refer 253 // to the function itself. 254 index := cp.thisScope().add(name + FnSuffix) 255 op := cp.lambda(bodyNode) 256 257 return fnOp{nameNode.Range(), index, op} 258 } 259 260 type fnOp struct { 261 keywordRange diag.Ranging 262 varIndex int 263 lambdaOp valuesOp 264 } 265 266 func (op fnOp) exec(fm *Frame) Exception { 267 // Initialize the function variable with the builtin nop function. This step 268 // allows the definition of recursive functions; the actual function will 269 // never be called. 270 fm.local.slots[op.varIndex].Set(NewGoFn("<shouldn't be called>", nop)) 271 values, exc := op.lambdaOp.exec(fm) 272 if exc != nil { 273 return exc 274 } 275 c := values[0].(*closure) 276 c.Op = fnWrap{c.Op} 277 return fm.errorp(op.keywordRange, fm.local.slots[op.varIndex].Set(c)) 278 } 279 280 type fnWrap struct{ effectOp } 281 282 func (op fnWrap) Range() diag.Ranging { return op.effectOp.(diag.Ranger).Range() } 283 284 func (op fnWrap) exec(fm *Frame) Exception { 285 exc := op.effectOp.exec(fm) 286 if exc != nil && exc.Reason() != Return { 287 // rethrow 288 return exc 289 } 290 return nil 291 } 292 293 // UseForm = 'use' StringPrimary 294 func compileUse(cp *compiler, fn *parse.Form) effectOp { 295 var name, spec string 296 297 switch len(fn.Args) { 298 case 0: 299 end := fn.Head.Range().To 300 cp.errorpf(diag.PointRanging(end), "lack module name") 301 case 1: 302 spec = mustString(cp, fn.Args[0], 303 "module spec should be a literal string") 304 // Use the last path component as the name; for instance, if path = 305 // "a/b/c/d", name is "d". If path doesn't have slashes, name = path. 306 name = spec[strings.LastIndexByte(spec, '/')+1:] 307 case 2: 308 // TODO(xiaq): Allow using variable as module path 309 spec = mustString(cp, fn.Args[0], 310 "module spec should be a literal string") 311 name = mustString(cp, fn.Args[1], 312 "module name should be a literal string") 313 default: // > 2 314 cp.errorpf(diag.MixedRanging(fn.Args[2], fn.Args[len(fn.Args)-1]), 315 "superfluous argument(s)") 316 } 317 318 return useOp{fn.Range(), cp.thisScope().add(name + NsSuffix), spec} 319 } 320 321 type useOp struct { 322 diag.Ranging 323 varIndex int 324 spec string 325 } 326 327 func (op useOp) exec(fm *Frame) Exception { 328 ns, err := use(fm, op.spec, op) 329 if err != nil { 330 return fm.errorp(op, err) 331 } 332 fm.local.slots[op.varIndex].Set(ns) 333 return nil 334 } 335 336 var bundledModules = bundled.Get() 337 338 func use(fm *Frame, spec string, r diag.Ranger) (*Ns, error) { 339 if strings.HasPrefix(spec, "./") || strings.HasPrefix(spec, "../") { 340 var dir string 341 if fm.srcMeta.IsFile { 342 dir = filepath.Dir(fm.srcMeta.Name) 343 } else { 344 var err error 345 dir, err = os.Getwd() 346 if err != nil { 347 return nil, err 348 } 349 } 350 path := filepath.Clean(dir + "/" + spec + ".elv") 351 return useFromFile(fm, spec, path, r) 352 } 353 if ns, ok := fm.Evaler.modules[spec]; ok { 354 return ns, nil 355 } 356 if code, ok := bundledModules[spec]; ok { 357 return evalModule(fm, spec, 358 parse.Source{Name: "[bundled " + spec + "]", Code: code}, r) 359 } 360 libDir := fm.Evaler.getLibDir() 361 if libDir == "" { 362 return nil, noSuchModule{spec} 363 } 364 return useFromFile(fm, spec, libDir+"/"+spec+".elv", r) 365 } 366 367 // TODO: Make access to fm.Evaler.modules concurrency-safe. 368 func useFromFile(fm *Frame, spec, path string, r diag.Ranger) (*Ns, error) { 369 if ns, ok := fm.Evaler.modules[path]; ok { 370 return ns, nil 371 } 372 code, err := readFileUTF8(path) 373 if err != nil { 374 if os.IsNotExist(err) { 375 return nil, noSuchModule{spec} 376 } 377 return nil, err 378 } 379 return evalModule(fm, path, parse.Source{Name: path, Code: code, IsFile: true}, r) 380 } 381 382 // TODO: Make access to fm.Evaler.modules concurrency-safe. 383 func evalModule(fm *Frame, key string, src parse.Source, r diag.Ranger) (*Ns, error) { 384 ns, exec, err := fm.PrepareEval(src, r, new(Ns)) 385 if err != nil { 386 return nil, err 387 } 388 // Installs the namespace before executing. This prevent circular use'es 389 // from resulting in an infinite recursion. 390 fm.Evaler.modules[key] = ns 391 err = exec() 392 if err != nil { 393 // Unload the namespace. 394 delete(fm.Evaler.modules, key) 395 return nil, err 396 } 397 return ns, nil 398 } 399 400 // compileAnd compiles the "and" special form. 401 // 402 // The and special form evaluates arguments until a false-ish values is found 403 // and outputs it; the remaining arguments are not evaluated. If there are no 404 // false-ish values, the last value is output. If there are no arguments, it 405 // outputs $true, as if there is a hidden $true before actual arguments. 406 func compileAnd(cp *compiler, fn *parse.Form) effectOp { 407 return &andOrOp{cp.compoundOps(fn.Args), true, false} 408 } 409 410 // compileOr compiles the "or" special form. 411 // 412 // The or special form evaluates arguments until a true-ish values is found and 413 // outputs it; the remaining arguments are not evaluated. If there are no 414 // true-ish values, the last value is output. If there are no arguments, it 415 // outputs $false, as if there is a hidden $false before actual arguments. 416 func compileOr(cp *compiler, fn *parse.Form) effectOp { 417 return &andOrOp{cp.compoundOps(fn.Args), false, true} 418 } 419 420 type andOrOp struct { 421 argOps []valuesOp 422 init bool 423 stopAt bool 424 } 425 426 func (op *andOrOp) exec(fm *Frame) Exception { 427 var lastValue interface{} = vals.Bool(op.init) 428 for _, argOp := range op.argOps { 429 values, exc := argOp.exec(fm) 430 if exc != nil { 431 return exc 432 } 433 for _, value := range values { 434 if vals.Bool(value) == op.stopAt { 435 fm.OutputChan() <- value 436 return nil 437 } 438 lastValue = value 439 } 440 } 441 fm.OutputChan() <- lastValue 442 return nil 443 } 444 445 func compileIf(cp *compiler, fn *parse.Form) effectOp { 446 args := cp.walkArgs(fn) 447 var condNodes []*parse.Compound 448 var bodyNodes []*parse.Primary 449 for { 450 condNodes = append(condNodes, args.next()) 451 bodyNodes = append(bodyNodes, args.nextMustLambda()) 452 if !args.nextIs("elif") { 453 break 454 } 455 } 456 elseNode := args.nextMustLambdaIfAfter("else") 457 args.mustEnd() 458 459 condOps := cp.compoundOps(condNodes) 460 bodyOps := cp.primaryOps(bodyNodes) 461 var elseOp valuesOp 462 if elseNode != nil { 463 elseOp = cp.primaryOp(elseNode) 464 } 465 466 return &ifOp{fn.Range(), condOps, bodyOps, elseOp} 467 } 468 469 type ifOp struct { 470 diag.Ranging 471 condOps []valuesOp 472 bodyOps []valuesOp 473 elseOp valuesOp 474 } 475 476 func (op *ifOp) exec(fm *Frame) Exception { 477 bodies := make([]Callable, len(op.bodyOps)) 478 for i, bodyOp := range op.bodyOps { 479 bodies[i] = execLambdaOp(fm, bodyOp) 480 } 481 elseFn := execLambdaOp(fm, op.elseOp) 482 for i, condOp := range op.condOps { 483 condValues, exc := condOp.exec(fm.fork("if cond")) 484 if exc != nil { 485 return exc 486 } 487 if allTrue(condValues) { 488 return fm.errorp(op, bodies[i].Call(fm.fork("if body"), NoArgs, NoOpts)) 489 } 490 } 491 if op.elseOp != nil { 492 return fm.errorp(op, elseFn.Call(fm.fork("if else"), NoArgs, NoOpts)) 493 } 494 return nil 495 } 496 497 func compileWhile(cp *compiler, fn *parse.Form) effectOp { 498 args := cp.walkArgs(fn) 499 condNode := args.next() 500 bodyNode := args.nextMustLambda() 501 elseNode := args.nextMustLambdaIfAfter("else") 502 args.mustEnd() 503 504 condOp := cp.compoundOp(condNode) 505 bodyOp := cp.primaryOp(bodyNode) 506 var elseOp valuesOp 507 if elseNode != nil { 508 elseOp = cp.primaryOp(elseNode) 509 } 510 511 return &whileOp{fn.Range(), condOp, bodyOp, elseOp} 512 } 513 514 type whileOp struct { 515 diag.Ranging 516 condOp, bodyOp, elseOp valuesOp 517 } 518 519 func (op *whileOp) exec(fm *Frame) Exception { 520 body := execLambdaOp(fm, op.bodyOp) 521 elseBody := execLambdaOp(fm, op.elseOp) 522 523 iterated := false 524 for { 525 condValues, exc := op.condOp.exec(fm.fork("while cond")) 526 if exc != nil { 527 return exc 528 } 529 if !allTrue(condValues) { 530 break 531 } 532 iterated = true 533 err := body.Call(fm.fork("while"), NoArgs, NoOpts) 534 if err != nil { 535 exc := err.(Exception) 536 if exc.Reason() == Continue { 537 // Do nothing 538 } else if exc.Reason() == Break { 539 break 540 } else { 541 return exc 542 } 543 } 544 } 545 546 if op.elseOp != nil && !iterated { 547 return fm.errorp(op, elseBody.Call(fm.fork("while else"), NoArgs, NoOpts)) 548 } 549 return nil 550 } 551 552 func compileFor(cp *compiler, fn *parse.Form) effectOp { 553 args := cp.walkArgs(fn) 554 varNode := args.next() 555 iterNode := args.next() 556 bodyNode := args.nextMustLambda() 557 elseNode := args.nextMustLambdaIfAfter("else") 558 args.mustEnd() 559 560 lvalue := cp.compileOneLValue(varNode) 561 562 iterOp := cp.compoundOp(iterNode) 563 bodyOp := cp.primaryOp(bodyNode) 564 var elseOp valuesOp 565 if elseNode != nil { 566 elseOp = cp.primaryOp(elseNode) 567 } 568 569 return &forOp{fn.Range(), lvalue, iterOp, bodyOp, elseOp} 570 } 571 572 type forOp struct { 573 diag.Ranging 574 lvalue lvalue 575 iterOp valuesOp 576 bodyOp valuesOp 577 elseOp valuesOp 578 } 579 580 func (op *forOp) exec(fm *Frame) Exception { 581 variable, err := derefLValue(fm, op.lvalue) 582 if err != nil { 583 return fm.errorp(op, err) 584 } 585 iterable, err := evalForValue(fm, op.iterOp, "value being iterated") 586 if err != nil { 587 return fm.errorp(op, err) 588 } 589 590 body := execLambdaOp(fm, op.bodyOp) 591 elseBody := execLambdaOp(fm, op.elseOp) 592 593 iterated := false 594 var errElement error 595 errIterate := vals.Iterate(iterable, func(v interface{}) bool { 596 iterated = true 597 err := variable.Set(v) 598 if err != nil { 599 errElement = err 600 return false 601 } 602 err = body.Call(fm.fork("for"), NoArgs, NoOpts) 603 if err != nil { 604 exc := err.(Exception) 605 if exc.Reason() == Continue { 606 // do nothing 607 } else if exc.Reason() == Break { 608 return false 609 } else { 610 errElement = err 611 return false 612 } 613 } 614 return true 615 }) 616 if errIterate != nil { 617 return fm.errorp(op, errIterate) 618 } 619 if errElement != nil { 620 return fm.errorp(op, errElement) 621 } 622 623 if !iterated && elseBody != nil { 624 return fm.errorp(op, elseBody.Call(fm.fork("for else"), NoArgs, NoOpts)) 625 } 626 return nil 627 } 628 629 func compileTry(cp *compiler, fn *parse.Form) effectOp { 630 logger.Println("compiling try") 631 args := cp.walkArgs(fn) 632 bodyNode := args.nextMustLambda() 633 logger.Printf("body is %q", parse.SourceText(bodyNode)) 634 var exceptVarNode *parse.Compound 635 var exceptNode *parse.Primary 636 if args.nextIs("except") { 637 logger.Println("except-ing") 638 n := args.peek() 639 // Is this a variable? 640 if len(n.Indexings) == 1 && n.Indexings[0].Head.Type == parse.Bareword { 641 exceptVarNode = n 642 args.next() 643 } 644 exceptNode = args.nextMustLambda() 645 } 646 elseNode := args.nextMustLambdaIfAfter("else") 647 finallyNode := args.nextMustLambdaIfAfter("finally") 648 args.mustEnd() 649 650 var exceptVar lvalue 651 var bodyOp, exceptOp, elseOp, finallyOp valuesOp 652 bodyOp = cp.primaryOp(bodyNode) 653 if exceptVarNode != nil { 654 exceptVar = cp.compileOneLValue(exceptVarNode) 655 } 656 if exceptNode != nil { 657 exceptOp = cp.primaryOp(exceptNode) 658 } 659 if elseNode != nil { 660 elseOp = cp.primaryOp(elseNode) 661 } 662 if finallyNode != nil { 663 finallyOp = cp.primaryOp(finallyNode) 664 } 665 666 return &tryOp{fn.Range(), bodyOp, exceptVar, exceptOp, elseOp, finallyOp} 667 } 668 669 type tryOp struct { 670 diag.Ranging 671 bodyOp valuesOp 672 exceptVar lvalue 673 exceptOp valuesOp 674 elseOp valuesOp 675 finallyOp valuesOp 676 } 677 678 func (op *tryOp) exec(fm *Frame) Exception { 679 body := execLambdaOp(fm, op.bodyOp) 680 var exceptVar vars.Var 681 if op.exceptVar.ref != nil { 682 var err error 683 exceptVar, err = derefLValue(fm, op.exceptVar) 684 if err != nil { 685 return fm.errorp(op, err) 686 } 687 } 688 except := execLambdaOp(fm, op.exceptOp) 689 elseFn := execLambdaOp(fm, op.elseOp) 690 finally := execLambdaOp(fm, op.finallyOp) 691 692 err := body.Call(fm.fork("try body"), NoArgs, NoOpts) 693 if err != nil { 694 if except != nil { 695 if exceptVar != nil { 696 err := exceptVar.Set(err.(Exception)) 697 if err != nil { 698 return fm.errorp(op, err) 699 } 700 } 701 err = except.Call(fm.fork("try except"), NoArgs, NoOpts) 702 } 703 } else { 704 if elseFn != nil { 705 err = elseFn.Call(fm.fork("try else"), NoArgs, NoOpts) 706 } 707 } 708 if finally != nil { 709 errFinally := finally.Call(fm.fork("try finally"), NoArgs, NoOpts) 710 if errFinally != nil { 711 // TODO: If err is not nil, this discards err. Use something similar 712 // to pipeline exception to expose both. 713 return fm.errorp(op, errFinally) 714 } 715 } 716 return fm.errorp(op, err) 717 } 718 719 func (cp *compiler) compileOneLValue(n *parse.Compound) lvalue { 720 if len(n.Indexings) != 1 { 721 cp.errorpf(n, "must be valid lvalue") 722 } 723 lvalues := cp.parseIndexingLValue(n.Indexings[0]) 724 if lvalues.rest != -1 { 725 cp.errorpf(lvalues.lvalues[lvalues.rest], "rest variable not allowed") 726 } 727 if len(lvalues.lvalues) != 1 { 728 cp.errorpf(n, "must be exactly one lvalue") 729 } 730 return lvalues.lvalues[0] 731 } 732 733 // Executes a valuesOp that is known to yield a lambda and returns the lambda. 734 // Returns nil if op is nil. 735 func execLambdaOp(fm *Frame, op valuesOp) Callable { 736 if op == nil { 737 return nil 738 } 739 values, exc := op.exec(fm) 740 if exc != nil { 741 panic("must not be erroneous") 742 } 743 return values[0].(Callable) 744 }