src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/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 "fmt" 21 "os" 22 "path/filepath" 23 "reflect" 24 "strings" 25 "unicode/utf8" 26 27 "src.elv.sh/pkg/diag" 28 "src.elv.sh/pkg/eval/vals" 29 "src.elv.sh/pkg/eval/vars" 30 "src.elv.sh/pkg/parse" 31 "src.elv.sh/pkg/parse/cmpd" 32 ) 33 34 type compileBuiltin func(*compiler, *parse.Form) effectOp 35 36 var builtinSpecials map[string]compileBuiltin 37 38 // IsBuiltinSpecial is the set of all names of builtin special forms. It is 39 // intended for external consumption, e.g. the syntax highlighter. 40 var IsBuiltinSpecial = map[string]bool{} 41 42 // NoSuchModule encodes an error where a module spec cannot be resolved. 43 type NoSuchModule struct{ spec string } 44 45 // Error implements the error interface. 46 func (err NoSuchModule) Error() string { return "no such module: " + err.spec } 47 48 // PluginLoadError wraps a plugin loading error. 49 type PluginLoadError struct { 50 spec string 51 err error 52 } 53 54 // Error implements the error interface. 55 func (err PluginLoadError) Error() string { 56 return "load as plugin: " + err.spec + ": " + err.err.Error() 57 } 58 59 // Unwrap returns the wrapped error. 60 func (err PluginLoadError) Unwrap() error { return err.err } 61 62 func init() { 63 // Needed to avoid initialization loop 64 builtinSpecials = map[string]compileBuiltin{ 65 "var": compileVar, 66 "set": compileSet, 67 "tmp": compileTmp, 68 "del": compileDel, 69 "fn": compileFn, 70 71 "use": compileUse, 72 73 "and": compileAnd, 74 "or": compileOr, 75 "coalesce": compileCoalesce, 76 77 "if": compileIf, 78 "while": compileWhile, 79 "for": compileFor, 80 "try": compileTry, 81 82 "pragma": compilePragma, 83 } 84 for name := range builtinSpecials { 85 IsBuiltinSpecial[name] = true 86 } 87 } 88 89 // VarForm = 'var' { VariablePrimary } [ '=' { Compound } ] 90 func compileVar(cp *compiler, fn *parse.Form) effectOp { 91 lhsArgs, rhs := compileLHSRHS(cp, fn) 92 lhs := cp.parseCompoundLValues(lhsArgs, newLValue) 93 if rhs == nil { 94 // Just create new variables, nothing extra to do at runtime. 95 return nopOp{} 96 } 97 return &assignOp{fn.Range(), lhs, rhs, false} 98 } 99 100 // SetForm = 'set' { LHS } '=' { Compound } 101 func compileSet(cp *compiler, fn *parse.Form) effectOp { 102 lhs, rhs := compileSetArgs(cp, fn) 103 return &assignOp{fn.Range(), lhs, rhs, false} 104 } 105 106 // TmpForm = 'tmp' { LHS } '=' { Compound } 107 func compileTmp(cp *compiler, fn *parse.Form) effectOp { 108 if len(cp.scopes) <= 1 { 109 cp.errorpf(fn, "tmp may only be used inside a function") 110 } 111 lhs, rhs := compileSetArgs(cp, fn) 112 return &assignOp{fn.Range(), lhs, rhs, true} 113 } 114 115 func compileSetArgs(cp *compiler, fn *parse.Form) (lvaluesGroup, valuesOp) { 116 lhsArgs, rhs := compileLHSRHS(cp, fn) 117 if rhs == nil { 118 cp.errorpf(diag.PointRanging(fn.Range().To), "need = and right-hand-side") 119 } 120 lhs := cp.parseCompoundLValues(lhsArgs, setLValue) 121 return lhs, rhs 122 } 123 124 func compileLHSRHS(cp *compiler, fn *parse.Form) ([]*parse.Compound, valuesOp) { 125 for i, cn := range fn.Args { 126 if parse.SourceText(cn) == "=" { 127 lhs := fn.Args[:i] 128 if i == len(fn.Args)-1 { 129 return lhs, nopValuesOp{diag.PointRanging(fn.Range().To)} 130 } 131 return lhs, seqValuesOp{ 132 diag.MixedRanging(fn.Args[i+1], fn.Args[len(fn.Args)-1]), 133 cp.compoundOps(fn.Args[i+1:])} 134 } 135 } 136 return fn.Args, nil 137 } 138 139 const delArgMsg = "arguments to del must be variable or variable elements" 140 141 // DelForm = 'del' { LHS } 142 func compileDel(cp *compiler, fn *parse.Form) effectOp { 143 var ops []effectOp 144 for _, cn := range fn.Args { 145 if len(cn.Indexings) != 1 { 146 cp.errorpf(cn, delArgMsg) 147 continue 148 } 149 head, indices := cn.Indexings[0].Head, cn.Indexings[0].Indices 150 if head.Type == parse.Variable { 151 cp.errorpf(cn, "arguments to del must omit the dollar sign") 152 continue 153 } else if !parse.ValidLHSVariable(head, false) { 154 cp.errorpf(cn, delArgMsg) 155 continue 156 } 157 158 qname := head.Value 159 var f effectOp 160 ref := resolveVarRef(cp, qname, nil) 161 if ref == nil { 162 cp.errorpf(cn, "no variable $%s", head.Value) 163 continue 164 } 165 if len(indices) == 0 { 166 if ref.scope == envScope { 167 f = delEnvVarOp{fn.Range(), ref.subNames[0]} 168 } else if ref.scope == localScope && len(ref.subNames) == 0 { 169 f = delLocalVarOp{ref.index} 170 cp.thisScope().infos[ref.index].deleted = true 171 } else { 172 cp.errorpf(cn, "only variables in the local scope or E: can be deleted") 173 continue 174 } 175 } else { 176 f = newDelElementOp(ref, head.Range().From, head.Range().To, cp.arrayOps(indices)) 177 } 178 ops = append(ops, f) 179 } 180 return seqOp{ops} 181 } 182 183 type delLocalVarOp struct{ index int } 184 185 func (op delLocalVarOp) exec(fm *Frame) Exception { 186 fm.local.slots[op.index] = nil 187 return nil 188 } 189 190 type delEnvVarOp struct { 191 diag.Ranging 192 name string 193 } 194 195 func (op delEnvVarOp) exec(fm *Frame) Exception { 196 return fm.errorp(op, os.Unsetenv(op.name)) 197 } 198 199 func newDelElementOp(ref *varRef, begin, headEnd int, indexOps []valuesOp) effectOp { 200 ends := make([]int, len(indexOps)+1) 201 ends[0] = headEnd 202 for i, op := range indexOps { 203 ends[i+1] = op.Range().To 204 } 205 return &delElemOp{ref, indexOps, begin, ends} 206 } 207 208 type delElemOp struct { 209 ref *varRef 210 indexOps []valuesOp 211 begin int 212 ends []int 213 } 214 215 func (op *delElemOp) Range() diag.Ranging { 216 return diag.Ranging{From: op.begin, To: op.ends[0]} 217 } 218 219 func (op *delElemOp) exec(fm *Frame) Exception { 220 var indices []any 221 for _, indexOp := range op.indexOps { 222 indexValues, exc := indexOp.exec(fm) 223 if exc != nil { 224 return exc 225 } 226 if len(indexValues) != 1 { 227 return fm.errorpf(indexOp, "index must evaluate to a single value in argument to del") 228 } 229 indices = append(indices, indexValues[0]) 230 } 231 err := vars.DelElement(deref(fm, op.ref), indices) 232 if err != nil { 233 if level := vars.ElementErrorLevel(err); level >= 0 { 234 return fm.errorp(diag.Ranging{From: op.begin, To: op.ends[level]}, err) 235 } 236 return fm.errorp(op, err) 237 } 238 return nil 239 } 240 241 // FnForm = 'fn' StringPrimary LambdaPrimary 242 // 243 // fn f { foobar } is a shorthand for set '&'f = { foobar }. 244 func compileFn(cp *compiler, fn *parse.Form) effectOp { 245 args := getArgs(cp, fn) 246 name := args.get(0, "name").stringLiteral() 247 bodyNode := args.get(1, "function body").lambda() 248 if !args.finish() { 249 return nil 250 } 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{fn.Args[0].Range(), index, op} 258 } 259 260 type fnOp struct { 261 nameRange 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.nameRange, 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 args := getArgs(cp, fn) 296 spec := args.get(0, "module spec").stringLiteral() 297 name := "" 298 if args.has(1) { 299 name = args.get(1, "module name").stringLiteral() 300 } else { 301 name = spec[strings.LastIndexByte(spec, '/')+1:] 302 } 303 if !args.finish() { 304 return nil 305 } 306 307 return useOp{fn.Range(), cp.thisScope().add(name + NsSuffix), spec} 308 } 309 310 type useOp struct { 311 diag.Ranging 312 varIndex int 313 spec string 314 } 315 316 func (op useOp) exec(fm *Frame) Exception { 317 ns, err := use(fm, op.spec, op) 318 if err != nil { 319 return fm.errorp(op, err) 320 } 321 fm.local.slots[op.varIndex].Set(ns) 322 return nil 323 } 324 325 // TODO: Add support for module specs relative to a package/workspace. 326 // See https://github.com/elves/elvish/issues/1421. 327 func use(fm *Frame, spec string, r diag.Ranger) (*Ns, error) { 328 // Handle relative imports. Note that this deliberately does not support Windows backslash as a 329 // path separator because module specs are meant to be platform independent. If necessary, we 330 // translate a module spec to an appropriate path for the platform. 331 if strings.HasPrefix(spec, "./") || strings.HasPrefix(spec, "../") { 332 var dir string 333 if fm.srcMeta.IsFile { 334 dir = filepath.Dir(fm.srcMeta.Name) 335 } else { 336 var err error 337 dir, err = os.Getwd() 338 if err != nil { 339 return nil, err 340 } 341 } 342 path := filepath.Clean(dir + "/" + spec) 343 return useFromFile(fm, spec, path, r) 344 } 345 346 // Handle imports of pre-defined modules like `builtin` and `str`. 347 if ns, ok := fm.Evaler.modules[spec]; ok { 348 return ns, nil 349 } 350 if code, ok := fm.Evaler.BundledModules[spec]; ok { 351 return evalModule(fm, spec, 352 parse.Source{Name: "[bundled " + spec + "]", Code: code}, r) 353 } 354 355 // Handle imports relative to the Elvish module search directories. 356 // 357 // TODO: For non-relative imports, use the spec (instead of the full path) 358 // as the module key instead to avoid searching every time. 359 for _, dir := range fm.Evaler.LibDirs { 360 ns, err := useFromFile(fm, spec, filepath.Join(dir, spec), r) 361 if _, isNoSuchModule := err.(NoSuchModule); isNoSuchModule { 362 continue 363 } 364 return ns, err 365 } 366 367 // Sadly, we couldn't resolve the module spec. 368 return nil, NoSuchModule{spec} 369 } 370 371 // TODO: Make access to fm.Evaler.modules concurrency-safe. 372 func useFromFile(fm *Frame, spec, path string, r diag.Ranger) (*Ns, error) { 373 if ns, ok := fm.Evaler.modules[path]; ok { 374 return ns, nil 375 } 376 _, err := os.Stat(path + ".so") 377 if err != nil { 378 code, err := readFileUTF8(path + ".elv") 379 if err != nil { 380 if os.IsNotExist(err) { 381 return nil, NoSuchModule{spec} 382 } 383 return nil, err 384 } 385 src := parse.Source{Name: path + ".elv", Code: code, IsFile: true} 386 return evalModule(fm, path, src, r) 387 } 388 389 plug, err := pluginOpen(path + ".so") 390 if err != nil { 391 return nil, PluginLoadError{spec, err} 392 } 393 sym, err := plug.Lookup("Ns") 394 if err != nil { 395 return nil, PluginLoadError{spec, err} 396 } 397 ns, ok := sym.(**Ns) 398 if !ok { 399 // plug.Lookup always returns a pointer 400 t := reflect.TypeOf(sym).Elem() 401 return nil, PluginLoadError{spec, fmt.Errorf("Ns symbol has wrong type %s", t)} 402 } 403 fm.Evaler.modules[path] = *ns 404 return *ns, nil 405 } 406 407 func readFileUTF8(fname string) (string, error) { 408 bytes, err := os.ReadFile(fname) 409 if err != nil { 410 return "", err 411 } 412 if !utf8.Valid(bytes) { 413 return "", fmt.Errorf("%s: source is not valid UTF-8", fname) 414 } 415 return string(bytes), nil 416 } 417 418 // TODO: Make access to fm.Evaler.modules concurrency-safe. 419 func evalModule(fm *Frame, key string, src parse.Source, r diag.Ranger) (*Ns, error) { 420 ns, exec, err := fm.PrepareEval(src, r, new(Ns)) 421 if err != nil { 422 return nil, err 423 } 424 // Installs the namespace before executing. This prevent circular use'es 425 // from resulting in an infinite recursion. 426 fm.Evaler.modules[key] = ns 427 err = exec() 428 if err != nil { 429 // Unload the namespace. 430 delete(fm.Evaler.modules, key) 431 return nil, err 432 } 433 return ns, nil 434 } 435 436 // compileAnd compiles the "and" special form. 437 // 438 // The and special form evaluates arguments until a false-ish values is found 439 // and outputs it; the remaining arguments are not evaluated. If there are no 440 // false-ish values, the last value is output. If there are no arguments, it 441 // outputs $true, as if there is a hidden $true before actual arguments. 442 func compileAnd(cp *compiler, fn *parse.Form) effectOp { 443 return &andOrOp{fn.Range(), cp.compoundOps(fn.Args), true, false} 444 } 445 446 // compileOr compiles the "or" special form. 447 // 448 // The or special form evaluates arguments until a true-ish values is found and 449 // outputs it; the remaining arguments are not evaluated. If there are no 450 // true-ish values, the last value is output. If there are no arguments, it 451 // outputs $false, as if there is a hidden $false before actual arguments. 452 func compileOr(cp *compiler, fn *parse.Form) effectOp { 453 return &andOrOp{fn.Range(), cp.compoundOps(fn.Args), false, true} 454 } 455 456 type andOrOp struct { 457 diag.Ranging 458 argOps []valuesOp 459 init bool 460 stopAt bool 461 } 462 463 func (op *andOrOp) exec(fm *Frame) Exception { 464 var lastValue any = vals.Bool(op.init) 465 out := fm.ValueOutput() 466 for _, argOp := range op.argOps { 467 values, exc := argOp.exec(fm) 468 if exc != nil { 469 return exc 470 } 471 for _, value := range values { 472 if vals.Bool(value) == op.stopAt { 473 return fm.errorp(op, out.Put(value)) 474 } 475 lastValue = value 476 } 477 } 478 return fm.errorp(op, out.Put(lastValue)) 479 } 480 481 // Compiles the "coalesce" special form, which is like "or", but evaluates until 482 // a non-nil value is found. 483 func compileCoalesce(cp *compiler, fn *parse.Form) effectOp { 484 return &coalesceOp{fn.Range(), cp.compoundOps(fn.Args)} 485 } 486 487 type coalesceOp struct { 488 diag.Ranging 489 argOps []valuesOp 490 } 491 492 func (op *coalesceOp) exec(fm *Frame) Exception { 493 out := fm.ValueOutput() 494 for _, argOp := range op.argOps { 495 values, exc := argOp.exec(fm) 496 if exc != nil { 497 return exc 498 } 499 for _, value := range values { 500 if value != nil { 501 return fm.errorp(op, out.Put(value)) 502 } 503 } 504 } 505 return fm.errorp(op, out.Put(nil)) 506 } 507 508 func compileIf(cp *compiler, fn *parse.Form) effectOp { 509 args := getArgs(cp, fn) 510 var condNodes []*parse.Compound 511 var bodyNodes []*parse.Primary 512 i := 0 513 bodyName := "if body" 514 for { 515 condNodes = append(condNodes, args.get(i, "condition").any()) 516 bodyNodes = append(bodyNodes, args.get(i+1, bodyName).thunk()) 517 i += 2 518 if !args.hasKeyword(i, "elif") { 519 break 520 } 521 i++ 522 bodyName = "elif body" 523 } 524 elseBody := args.optionalKeywordBody(i, "else") 525 if !args.finish() { 526 return nil 527 } 528 529 condOps := cp.compoundOps(condNodes) 530 bodyOps := cp.primaryOps(bodyNodes) 531 var elseOp valuesOp 532 if elseBody != nil { 533 elseOp = cp.primaryOp(elseBody) 534 } 535 536 return &ifOp{fn.Range(), condOps, bodyOps, elseOp} 537 } 538 539 type ifOp struct { 540 diag.Ranging 541 condOps []valuesOp 542 bodyOps []valuesOp 543 elseOp valuesOp 544 } 545 546 func (op *ifOp) exec(fm *Frame) Exception { 547 bodies := make([]Callable, len(op.bodyOps)) 548 for i, bodyOp := range op.bodyOps { 549 bodies[i] = execLambdaOp(fm, bodyOp) 550 } 551 elseFn := execLambdaOp(fm, op.elseOp) 552 for i, condOp := range op.condOps { 553 condValues, exc := condOp.exec(fm.Fork("if cond")) 554 if exc != nil { 555 return exc 556 } 557 if allTrue(condValues) { 558 return fm.errorp(op, bodies[i].Call(fm.Fork("if body"), NoArgs, NoOpts)) 559 } 560 } 561 if op.elseOp != nil { 562 return fm.errorp(op, elseFn.Call(fm.Fork("if else"), NoArgs, NoOpts)) 563 } 564 return nil 565 } 566 567 func compileWhile(cp *compiler, fn *parse.Form) effectOp { 568 args := getArgs(cp, fn) 569 condNode := args.get(0, "condition").any() 570 bodyNode := args.get(1, "while body").thunk() 571 elseNode := args.optionalKeywordBody(2, "else") 572 if !args.finish() { 573 return nil 574 } 575 576 condOp := cp.compoundOp(condNode) 577 bodyOp := cp.primaryOp(bodyNode) 578 var elseOp valuesOp 579 if elseNode != nil { 580 elseOp = cp.primaryOp(elseNode) 581 } 582 583 return &whileOp{fn.Range(), condOp, bodyOp, elseOp} 584 } 585 586 type whileOp struct { 587 diag.Ranging 588 condOp, bodyOp, elseOp valuesOp 589 } 590 591 func (op *whileOp) exec(fm *Frame) Exception { 592 body := execLambdaOp(fm, op.bodyOp) 593 elseBody := execLambdaOp(fm, op.elseOp) 594 595 iterated := false 596 for { 597 condValues, exc := op.condOp.exec(fm.Fork("while cond")) 598 if exc != nil { 599 return exc 600 } 601 if !allTrue(condValues) { 602 break 603 } 604 iterated = true 605 err := body.Call(fm.Fork("while"), NoArgs, NoOpts) 606 if err != nil { 607 exc := err.(Exception) 608 if exc.Reason() == Continue { 609 // Do nothing 610 } else if exc.Reason() == Break { 611 break 612 } else { 613 return exc 614 } 615 } 616 } 617 618 if op.elseOp != nil && !iterated { 619 return fm.errorp(op, elseBody.Call(fm.Fork("while else"), NoArgs, NoOpts)) 620 } 621 return nil 622 } 623 624 func compileFor(cp *compiler, fn *parse.Form) effectOp { 625 args := getArgs(cp, fn) 626 varNode := args.get(0, "variable").any() 627 iterNode := args.get(1, "iterable").any() 628 bodyNode := args.get(2, "for body").thunk() 629 elseNode := args.optionalKeywordBody(3, "else") 630 if !args.finish() { 631 return nil 632 } 633 634 lvalue := cp.compileOneLValue(varNode, setLValue|newLValue) 635 636 iterOp := cp.compoundOp(iterNode) 637 bodyOp := cp.primaryOp(bodyNode) 638 var elseOp valuesOp 639 if elseNode != nil { 640 elseOp = cp.primaryOp(elseNode) 641 } 642 643 return &forOp{fn.Range(), lvalue, iterOp, bodyOp, elseOp} 644 } 645 646 type forOp struct { 647 diag.Ranging 648 lvalue lvalue 649 iterOp valuesOp 650 bodyOp valuesOp 651 elseOp valuesOp 652 } 653 654 func (op *forOp) exec(fm *Frame) Exception { 655 variable, err := derefLValue(fm, op.lvalue) 656 if err != nil { 657 return fm.errorp(op.lvalue, err) 658 } 659 iterable, err := evalForValue(fm, op.iterOp, "value being iterated") 660 if err != nil { 661 return fm.errorp(op, err) 662 } 663 664 body := execLambdaOp(fm, op.bodyOp) 665 elseBody := execLambdaOp(fm, op.elseOp) 666 667 iterated := false 668 var errElement error 669 errIterate := vals.Iterate(iterable, func(v any) bool { 670 iterated = true 671 err := variable.Set(v) 672 if err != nil { 673 errElement = err 674 return false 675 } 676 err = body.Call(fm.Fork("for"), NoArgs, NoOpts) 677 if err != nil { 678 exc := err.(Exception) 679 if exc.Reason() == Continue { 680 // do nothing 681 } else if exc.Reason() == Break { 682 return false 683 } else { 684 errElement = err 685 return false 686 } 687 } 688 return true 689 }) 690 if errIterate != nil { 691 return fm.errorp(op, errIterate) 692 } 693 if errElement != nil { 694 return fm.errorp(op, errElement) 695 } 696 697 if !iterated && elseBody != nil { 698 return fm.errorp(op, elseBody.Call(fm.Fork("for else"), NoArgs, NoOpts)) 699 } 700 return nil 701 } 702 703 func compileTry(cp *compiler, fn *parse.Form) effectOp { 704 args := getArgs(cp, fn) 705 bodyNode := args.get(0, "try body").thunk() 706 i := 1 707 var catchVarNode *parse.Compound 708 var catchNode *parse.Primary 709 if args.hasKeyword(i, "catch") { 710 i++ 711 // Parse an optional lvalue into exceptVarNode. 712 n := args.get(i, "variable or body").any() 713 if _, ok := cmpd.StringLiteral(n); ok { 714 catchVarNode = n 715 i++ 716 } 717 catchNode = args.get(i, "catch body").thunk() 718 i++ 719 } 720 elseNode := args.optionalKeywordBody(i, "else") 721 if elseNode != nil { 722 i += 2 723 } 724 finallyNode := args.optionalKeywordBody(i, "finally") 725 if !args.finish() { 726 return nil 727 } 728 729 if elseNode != nil && catchNode == nil { 730 cp.errorpf(fn, "try with an else block requires a catch block") 731 } else if catchNode == nil && finallyNode == nil { 732 cp.errorpf(fn, "try must be followed by a catch block or a finally block") 733 } 734 735 var catchVar lvalue 736 var bodyOp, catchOp, elseOp, finallyOp valuesOp 737 bodyOp = cp.primaryOp(bodyNode) 738 if catchVarNode != nil { 739 catchVar = cp.compileOneLValue(catchVarNode, setLValue|newLValue) 740 } 741 if catchNode != nil { 742 catchOp = cp.primaryOp(catchNode) 743 } 744 if elseNode != nil { 745 elseOp = cp.primaryOp(elseNode) 746 } 747 if finallyNode != nil { 748 finallyOp = cp.primaryOp(finallyNode) 749 } 750 751 return &tryOp{fn.Range(), bodyOp, catchVar, catchOp, elseOp, finallyOp} 752 } 753 754 type tryOp struct { 755 diag.Ranging 756 bodyOp valuesOp 757 catchVar lvalue 758 catchOp valuesOp 759 elseOp valuesOp 760 finallyOp valuesOp 761 } 762 763 func (op *tryOp) exec(fm *Frame) Exception { 764 body := execLambdaOp(fm, op.bodyOp) 765 var exceptVar vars.Var 766 if op.catchVar.ref != nil { 767 var err error 768 exceptVar, err = derefLValue(fm, op.catchVar) 769 if err != nil { 770 return fm.errorp(op, err) 771 } 772 } 773 catch := execLambdaOp(fm, op.catchOp) 774 elseFn := execLambdaOp(fm, op.elseOp) 775 finally := execLambdaOp(fm, op.finallyOp) 776 777 err := body.Call(fm.Fork("try body"), NoArgs, NoOpts) 778 if err != nil { 779 if catch != nil { 780 if exceptVar != nil { 781 err := exceptVar.Set(err.(Exception)) 782 if err != nil { 783 return fm.errorp(op.catchVar, err) 784 } 785 } 786 err = catch.Call(fm.Fork("try catch"), NoArgs, NoOpts) 787 } 788 } else { 789 if elseFn != nil { 790 err = elseFn.Call(fm.Fork("try else"), NoArgs, NoOpts) 791 } 792 } 793 if finally != nil { 794 errFinally := finally.Call(fm.Fork("try finally"), NoArgs, NoOpts) 795 if errFinally != nil { 796 // TODO: If err is not nil, this discards err. Use something similar 797 // to pipeline exception to expose both. 798 return fm.errorp(op, errFinally) 799 } 800 } 801 return fm.errorp(op, err) 802 } 803 804 // PragmaForm = 'pragma' 'fallback-resolver' '=' { Compound } 805 func compilePragma(cp *compiler, fn *parse.Form) effectOp { 806 args := getArgs(cp, fn) 807 name := args.get(0, "pragma name").stringLiteral() 808 eq := args.get(1, "literal =").stringLiteral() 809 if args.has(1) && eq != "=" { 810 args.errorpf(fn.Args[1], "must be literal =") 811 } 812 valueNode := args.get(2, "pragma value").any() 813 if !args.finish() { 814 return nil 815 } 816 817 switch name { 818 case "unknown-command": 819 value := stringLiteralOrError(cp, valueNode, "value for unknown-command") 820 switch value { 821 case "disallow": 822 cp.currentPragma().unknownCommandIsExternal = false 823 case "external": 824 cp.currentPragma().unknownCommandIsExternal = true 825 default: 826 cp.errorpf(valueNode, 827 "invalid value for unknown-command: %s", parse.Quote(value)) 828 } 829 default: 830 cp.errorpf(fn.Args[0], "unknown pragma %s", parse.Quote(name)) 831 } 832 return nopOp{} 833 } 834 835 func (cp *compiler) compileOneLValue(n *parse.Compound, f lvalueFlag) lvalue { 836 if len(n.Indexings) != 1 { 837 cp.errorpf(n, "must be valid lvalue") 838 } 839 lvalues := cp.parseIndexingLValue(n.Indexings[0], f) 840 if lvalues.rest != -1 { 841 cp.errorpf(lvalues.lvalues[lvalues.rest], "rest variable not allowed") 842 } 843 if len(lvalues.lvalues) != 1 { 844 cp.errorpf(n, "must be exactly one lvalue") 845 } 846 return lvalues.lvalues[0] 847 } 848 849 // Executes a valuesOp that is known to yield a lambda and returns the lambda. 850 // Returns nil if op is nil. 851 func execLambdaOp(fm *Frame, op valuesOp) Callable { 852 if op == nil { 853 return nil 854 } 855 values, exc := op.exec(fm) 856 if exc != nil { 857 panic("must not be erroneous") 858 } 859 return values[0].(Callable) 860 }