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  }