github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/node_utils.go (about) 1 package eval 2 3 import ( 4 "src.elv.sh/pkg/diag" 5 "src.elv.sh/pkg/parse" 6 "src.elv.sh/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 func (aw *argsWalker) nextMustLambdaIfAfter(leader string) *parse.Primary { 73 if aw.nextIs(leader) { 74 return aw.nextMustLambda(leader + " body") 75 } 76 return nil 77 } 78 79 func (aw *argsWalker) mustEnd() { 80 if aw.more() { 81 aw.cp.errorpf(diag.Ranging{From: aw.form.Args[aw.idx].Range().From, To: aw.form.Range().To}, "too many arguments") 82 } 83 }