mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-12 17:27:50 +08:00
Enforce blocks in control forms to be lambdas. Make except-var optional.
This commit is contained in:
parent
28fe942cad
commit
59d2bd580c
|
@ -16,12 +16,17 @@ func (aw *argsWalker) more() bool {
|
|||
return aw.idx < len(aw.form.Args)
|
||||
}
|
||||
|
||||
func (aw *argsWalker) next() *parse.Compound {
|
||||
func (aw *argsWalker) peek() *parse.Compound {
|
||||
if !aw.more() {
|
||||
aw.cp.errorpf(aw.form.End(), aw.form.End(), "need more arguments")
|
||||
}
|
||||
return aw.form.Args[aw.idx]
|
||||
}
|
||||
|
||||
func (aw *argsWalker) next() *parse.Compound {
|
||||
n := aw.peek()
|
||||
aw.idx++
|
||||
return aw.form.Args[aw.idx-1]
|
||||
return n
|
||||
}
|
||||
|
||||
// nextIs returns whether the next argument's source matches the given text. It
|
||||
|
@ -34,9 +39,26 @@ func (aw *argsWalker) nextIs(text string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (aw *argsWalker) nextLedBy(leader string) *parse.Compound {
|
||||
// nextMustLambda fetches the next argument, raising an error if it is not a
|
||||
// lambda.
|
||||
func (aw *argsWalker) nextMustLambda() *parse.Primary {
|
||||
n := aw.next()
|
||||
if len(n.Indexings) != 1 {
|
||||
aw.cp.errorpf(n.Begin(), n.End(), "must be lambda")
|
||||
}
|
||||
if len(n.Indexings[0].Indicies) != 0 {
|
||||
aw.cp.errorpf(n.Begin(), n.End(), "must be lambda")
|
||||
}
|
||||
pn := n.Indexings[0].Head
|
||||
if pn.Type != parse.Lambda {
|
||||
aw.cp.errorpf(n.Begin(), n.End(), "must be lambda")
|
||||
}
|
||||
return pn
|
||||
}
|
||||
|
||||
func (aw *argsWalker) nextMustLambdaIfAfter(leader string) *parse.Primary {
|
||||
if aw.nextIs(leader) {
|
||||
return aw.next()
|
||||
return aw.nextMustLambda()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -226,30 +226,31 @@ func use(ec *EvalCtx, modname string, pfilename *string) {
|
|||
|
||||
func compileIf(cp *compiler, fn *parse.Form) OpFunc {
|
||||
args := cp.walkArgs(fn)
|
||||
var condNodes, bodyNodes []*parse.Compound
|
||||
var condNodes []*parse.Compound
|
||||
var bodyNodes []*parse.Primary
|
||||
for {
|
||||
condNodes = append(condNodes, args.next())
|
||||
bodyNodes = append(bodyNodes, args.next())
|
||||
bodyNodes = append(bodyNodes, args.nextMustLambda())
|
||||
if !args.nextIs("elif") {
|
||||
break
|
||||
}
|
||||
}
|
||||
elseNode := args.nextLedBy("else")
|
||||
elseNode := args.nextMustLambdaIfAfter("else")
|
||||
args.mustEnd()
|
||||
|
||||
condOps := cp.compoundOps(condNodes)
|
||||
bodyOps := cp.compoundOps(bodyNodes)
|
||||
bodyOps := cp.primaryOps(bodyNodes)
|
||||
var elseOp ValuesOp
|
||||
if elseNode != nil {
|
||||
elseOp = cp.compoundOp(elseNode)
|
||||
elseOp = cp.primaryOp(elseNode)
|
||||
}
|
||||
|
||||
return func(ec *EvalCtx) {
|
||||
bodies := make([]Callable, len(bodyOps))
|
||||
for i, bodyOp := range bodyOps {
|
||||
bodies[i] = bodyOp.execMustOneFn(ec)
|
||||
bodies[i] = bodyOp.execlambdaOp(ec)
|
||||
}
|
||||
else_ := elseOp.execMustOneFn(ec)
|
||||
else_ := elseOp.execlambdaOp(ec)
|
||||
for i, condOp := range condOps {
|
||||
if allTrue(condOp.Exec(ec)) {
|
||||
bodies[i].Call(ec, NoArgs, NoOpts)
|
||||
|
@ -265,14 +266,14 @@ func compileIf(cp *compiler, fn *parse.Form) OpFunc {
|
|||
func compileWhile(cp *compiler, fn *parse.Form) OpFunc {
|
||||
args := cp.walkArgs(fn)
|
||||
condNode := args.next()
|
||||
bodyNode := args.next()
|
||||
bodyNode := args.nextMustLambda()
|
||||
args.mustEnd()
|
||||
|
||||
condOp := cp.compoundOp(condNode)
|
||||
bodyOp := cp.compoundOp(bodyNode)
|
||||
bodyOp := cp.primaryOp(bodyNode)
|
||||
|
||||
return func(ec *EvalCtx) {
|
||||
body := bodyOp.execMustOneFn(ec)
|
||||
body := bodyOp.execlambdaOp(ec)
|
||||
|
||||
for {
|
||||
cond := condOp.Exec(ec)
|
||||
|
@ -298,8 +299,8 @@ func compileFor(cp *compiler, fn *parse.Form) OpFunc {
|
|||
args := cp.walkArgs(fn)
|
||||
varNode := args.next()
|
||||
iterNode := args.next()
|
||||
bodyNode := args.next()
|
||||
elseNode := args.nextLedBy("else")
|
||||
bodyNode := args.nextMustLambda()
|
||||
elseNode := args.nextMustLambdaIfAfter("else")
|
||||
args.mustEnd()
|
||||
|
||||
varOp, restOp := cp.lvaluesOp(varNode.Indexings[0])
|
||||
|
@ -308,10 +309,10 @@ func compileFor(cp *compiler, fn *parse.Form) OpFunc {
|
|||
}
|
||||
|
||||
iterOp := cp.compoundOp(iterNode)
|
||||
bodyOp := cp.compoundOp(bodyNode)
|
||||
bodyOp := cp.primaryOp(bodyNode)
|
||||
var elseOp ValuesOp
|
||||
if elseNode != nil {
|
||||
elseOp = cp.compoundOp(elseNode)
|
||||
elseOp = cp.primaryOp(elseNode)
|
||||
}
|
||||
|
||||
return func(ec *EvalCtx) {
|
||||
|
@ -330,8 +331,8 @@ func compileFor(cp *compiler, fn *parse.Form) OpFunc {
|
|||
ec.errorpf(iterOp.Begin, iterOp.End, "should be one iterable")
|
||||
}
|
||||
|
||||
body := bodyOp.execMustOneFn(ec)
|
||||
elseBody := elseOp.execMustOneFn(ec)
|
||||
body := bodyOp.execlambdaOp(ec)
|
||||
elseBody := elseOp.execlambdaOp(ec)
|
||||
|
||||
iterated := false
|
||||
iterable.Iterate(func(v Value) bool {
|
||||
|
@ -360,48 +361,48 @@ func compileFor(cp *compiler, fn *parse.Form) OpFunc {
|
|||
func compileTry(cp *compiler, fn *parse.Form) OpFunc {
|
||||
Logger.Println("compiling try")
|
||||
args := cp.walkArgs(fn)
|
||||
bodyNode := args.next()
|
||||
bodyNode := args.nextMustLambda()
|
||||
Logger.Printf("body is %q", bodyNode.SourceText())
|
||||
var exceptVarNode *parse.Indexing
|
||||
var exceptNode *parse.Compound
|
||||
var exceptNode *parse.Primary
|
||||
if args.nextIs("except") {
|
||||
Logger.Println("except-ing")
|
||||
n := args.next()
|
||||
if len(n.Indexings) != 1 {
|
||||
cp.errorpf(n.Begin(), n.End(), "should be one variable")
|
||||
n := args.peek()
|
||||
// Is this a variable?
|
||||
if len(n.Indexings) == 1 && n.Indexings[0].Head.Type == parse.Bareword {
|
||||
exceptVarNode = n.Indexings[0]
|
||||
args.next()
|
||||
}
|
||||
exceptVarNode = n.Indexings[0]
|
||||
exceptNode = args.next()
|
||||
Logger.Printf("except-var = %q, except = %q", exceptVarNode.SourceText(), exceptNode.SourceText())
|
||||
exceptNode = args.nextMustLambda()
|
||||
}
|
||||
elseNode := args.nextLedBy("else")
|
||||
finallyNode := args.nextLedBy("finally")
|
||||
elseNode := args.nextMustLambdaIfAfter("else")
|
||||
finallyNode := args.nextMustLambdaIfAfter("finally")
|
||||
args.mustEnd()
|
||||
|
||||
var exceptVarOp LValuesOp
|
||||
var bodyOp, exceptOp, elseOp, finallyOp ValuesOp
|
||||
bodyOp = cp.compoundOp(bodyNode)
|
||||
bodyOp = cp.primaryOp(bodyNode)
|
||||
if exceptVarNode != nil {
|
||||
var restOp LValuesOp
|
||||
exceptVarOp, restOp = cp.lvaluesOp(exceptVarNode)
|
||||
if restOp.Func != nil {
|
||||
cp.errorpf(restOp.Begin, restOp.End, "may not use @rest in except variable")
|
||||
}
|
||||
exceptOp = cp.compoundOp(exceptNode)
|
||||
exceptOp = cp.primaryOp(exceptNode)
|
||||
}
|
||||
if elseNode != nil {
|
||||
elseOp = cp.compoundOp(elseNode)
|
||||
elseOp = cp.primaryOp(elseNode)
|
||||
}
|
||||
if finallyNode != nil {
|
||||
finallyOp = cp.compoundOp(finallyNode)
|
||||
finallyOp = cp.primaryOp(finallyNode)
|
||||
}
|
||||
|
||||
return func(ec *EvalCtx) {
|
||||
body := bodyOp.execMustOneFn(ec)
|
||||
body := bodyOp.execlambdaOp(ec)
|
||||
exceptVar := exceptVarOp.execMustOne(ec)
|
||||
except := exceptOp.execMustOneFn(ec)
|
||||
else_ := elseOp.execMustOneFn(ec)
|
||||
finally := finallyOp.execMustOneFn(ec)
|
||||
except := exceptOp.execlambdaOp(ec)
|
||||
else_ := elseOp.execlambdaOp(ec)
|
||||
finally := finallyOp.execlambdaOp(ec)
|
||||
|
||||
err := ec.PCall(body, NoArgs, NoOpts)
|
||||
if err != nil {
|
||||
|
@ -423,22 +424,14 @@ func compileTry(cp *compiler, fn *parse.Form) OpFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// execMustOneFn executes the ValuesOp and raises an exception if it does not
|
||||
// evaluate to exactly one Fn. If the given ValuesOp is empty, it returns nil.
|
||||
func (op ValuesOp) execMustOneFn(ec *EvalCtx) Callable {
|
||||
// execLambdaOp executes a ValuesOp that is known to yield a lambda and returns
|
||||
// the lambda. If the ValuesOp is empty, it returns a nil.
|
||||
func (op ValuesOp) execlambdaOp(ec *EvalCtx) Callable {
|
||||
if op.Func == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := op.Exec(ec)
|
||||
if len(values) != 1 {
|
||||
ec.errorpf(op.Begin, op.End, "should be one fn")
|
||||
}
|
||||
fn, ok := values[0].(Callable)
|
||||
if !ok {
|
||||
ec.errorpf(op.Begin, op.End, "should be one fn")
|
||||
}
|
||||
return fn
|
||||
return op.Exec(ec)[0].(Callable)
|
||||
}
|
||||
|
||||
// execMustOne executes the LValuesOp and raises an exception if it does not
|
||||
|
|
|
@ -79,7 +79,7 @@ var evalTests = []struct {
|
|||
{"if $false { put 2 } elif true { put 2 } else { put 3 }",
|
||||
strs("2"), nomore},
|
||||
// try
|
||||
{"try { nop } except - { put bad } else { put good }", strs("good"), nomore},
|
||||
{"try { nop } except { put bad } else { put good }", strs("good"), nomore},
|
||||
{"try { e:false } except - { put bad } else { put good }", strs("bad"), nomore},
|
||||
// while
|
||||
{"x=0; while (< $x 4) { put $x; x=(+ $x 1) }",
|
||||
|
|
Loading…
Reference in New Issue
Block a user