github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/node_utils.go (about)

     1  package eval
     2  
     3  import (
     4  	"github.com/markusbkk/elvish/pkg/diag"
     5  	"github.com/markusbkk/elvish/pkg/parse"
     6  	"github.com/markusbkk/elvish/pkg/parse/cmpd"
     7  )
     8  
     9  // Utilities for working with nodes.
    10  
    11  func stringLiteralOrError(cp *compiler, n *parse.Compound, what string) string {
    12  	s, err := cmpd.StringLiteralOrError(n, what)
    13  	if err != nil {
    14  		cp.errorpf(n, "%v", err)
    15  	}
    16  	return s
    17  }
    18  
    19  type errorpfer interface {
    20  	errorpf(r diag.Ranger, fmt string, args ...interface{})
    21  }
    22  
    23  // argsWalker is used by builtin special forms to implement argument parsing.
    24  type argsWalker struct {
    25  	cp   errorpfer
    26  	form *parse.Form
    27  	idx  int
    28  }
    29  
    30  func (cp *compiler) walkArgs(f *parse.Form) *argsWalker {
    31  	return &argsWalker{cp, f, 0}
    32  }
    33  
    34  func (aw *argsWalker) more() bool {
    35  	return aw.idx < len(aw.form.Args)
    36  }
    37  
    38  func (aw *argsWalker) peek() *parse.Compound {
    39  	if !aw.more() {
    40  		aw.cp.errorpf(aw.form, "need more arguments")
    41  	}
    42  	return aw.form.Args[aw.idx]
    43  }
    44  
    45  func (aw *argsWalker) next() *parse.Compound {
    46  	n := aw.peek()
    47  	aw.idx++
    48  	return n
    49  }
    50  
    51  // nextIs returns whether the next argument's source matches the given text. It
    52  // also consumes the argument if it is.
    53  func (aw *argsWalker) nextIs(text string) bool {
    54  	if aw.more() && parse.SourceText(aw.form.Args[aw.idx]) == text {
    55  		aw.idx++
    56  		return true
    57  	}
    58  	return false
    59  }
    60  
    61  // nextMustLambda fetches the next argument, raising an error if it is not a
    62  // lambda.
    63  func (aw *argsWalker) nextMustLambda(what string) *parse.Primary {
    64  	n := aw.next()
    65  	pn, ok := cmpd.Lambda(n)
    66  	if !ok {
    67  		aw.cp.errorpf(n, "%s must be lambda, found %s", what, cmpd.Shape(n))
    68  	}
    69  	return pn
    70  }
    71  
    72  // nextMustThunk fetches the next argument, raising an error if it is not a
    73  // thunk.
    74  func (aw *argsWalker) nextMustThunk(what string) *parse.Primary {
    75  	n := aw.nextMustLambda(what)
    76  	if len(n.Elements) > 0 {
    77  		aw.cp.errorpf(n, "%s must not have arguments", what)
    78  	}
    79  	if len(n.MapPairs) > 0 {
    80  		aw.cp.errorpf(n, "%s must not have options", what)
    81  	}
    82  	return n
    83  }
    84  
    85  func (aw *argsWalker) nextMustThunkIfAfter(leader string) *parse.Primary {
    86  	if aw.nextIs(leader) {
    87  		return aw.nextMustLambda(leader + " body")
    88  	}
    89  	return nil
    90  }
    91  
    92  func (aw *argsWalker) mustEnd() {
    93  	if aw.more() {
    94  		aw.cp.errorpf(diag.Ranging{From: aw.form.Args[aw.idx].Range().From, To: aw.form.Range().To}, "too many arguments")
    95  	}
    96  }