for {
switch token := t.nextNonSpace(); token.typ {
case itemRightDelim, itemRightParen:
- if len(pipe.Cmds) == 0 {
- t.errorf("missing value for %s", context)
- }
+ // At this point, the pipeline is complete
+ t.checkPipeline(pipe, context)
if token.typ == itemRightParen {
t.backup()
}
}
}
+func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
+ // Reject empty pipelines
+ if len(pipe.Cmds) == 0 {
+ t.errorf("missing value for %s", context)
+ }
+ // Only the first command of a pipeline can start with a non executable operand
+ for i, c := range pipe.Cmds[1:] {
+ switch c.Args[0].Type() {
+ case NodeBool, NodeDot, NodeNil, NodeNumber, NodeString:
+ // With A|B|C, pipeline stage 2 is B
+ t.errorf("non executable command in pipeline stage %d", i+2)
+ }
+ }
+}
+
func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
defer t.popVars(len(t.vars))
line = t.lex.lineNumber()
{"dot after string", `{{"hello".guys}}`, hasError, ""},
{"dot after dot", "{{..E}}", hasError, ""},
{"dot after nil", "{{nil.E}}", hasError, ""},
+ // Wrong pipeline
+ {"wrong pipeline dot", "{{12|.}}", hasError, ""},
+ {"wrong pipeline number", "{{.|12|printf}}", hasError, ""},
+ {"wrong pipeline string", "{{.|print|\"error\"}}", hasError, ""},
+ {"wrong pipeline char", "{{12|print|html|'e'}}", hasError, ""},
+ {"wrong pipeline boolean", "{{.|true}}", hasError, ""},
+ {"wrong pipeline nil", "{{'c'|nil}}", hasError, ""},
+ {"empty pipeline", `{{printf "%d" ( ) }}`, hasError, ""},
}
var builtins = map[string]interface{}{
{"wrongdot",
"{{true.any}}",
hasError, `unexpected . after term`},
+ {"wrongpipeline",
+ "{{12|false}}",
+ hasError, `non executable command in pipeline`},
+ {"emptypipeline",
+ `{{ ( ) }}`,
+ hasError, `missing value for parenthesized pipeline`},
}
func TestErrors(t *testing.T) {