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  }