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  }