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