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 }