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