mirror of
https://github.com/Stichting-MINIX-Research-Foundation/pkgsrc-ng.git
synced 2025-09-22 11:04:51 -04:00
636 lines
19 KiB
Go
636 lines
19 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"gopkg.in/check.v1"
|
|
"strconv"
|
|
)
|
|
|
|
type ShSuite struct {
|
|
c *check.C
|
|
}
|
|
|
|
var _ = check.Suite(&ShSuite{})
|
|
|
|
func (s *ShSuite) Test_ShellParser_program(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("",
|
|
b.List())
|
|
|
|
s.test("echo ;",
|
|
b.List().AddCommand(b.SimpleCommand("echo")).AddSemicolon())
|
|
|
|
s.test("echo",
|
|
b.List().AddCommand(b.SimpleCommand("echo")))
|
|
|
|
s.test(""+
|
|
"cd ${WRKSRC} && ${FIND} ${${_list_}} -type f ! -name '*.orig' 2> /dev/null "+
|
|
"| pax -rw -pm ${DESTDIR}${PREFIX}/${${_dir_}}",
|
|
b.List().AddAndOr(b.AndOr(
|
|
b.Pipeline(false, b.SimpleCommand("cd", "${WRKSRC}"))).Add("&&",
|
|
b.Pipeline(false,
|
|
b.SimpleCommand("${FIND}", "${${_list_}}", "-type", "f", "!", "-name", "'*.orig'", "2>/dev/null"),
|
|
b.SimpleCommand("pax", "-rw", "-pm", "${DESTDIR}${PREFIX}/${${_dir_}}")))))
|
|
|
|
s.test("${CAT} ${PKGDIR}/PLIST | while read entry ; do : ; done",
|
|
b.List().AddAndOr(b.AndOr(b.Pipeline(false,
|
|
b.SimpleCommand("${CAT}", "${PKGDIR}/PLIST"),
|
|
b.While(
|
|
b.List().AddCommand(b.SimpleCommand("read", "entry")).AddSemicolon(),
|
|
b.List().AddCommand(b.SimpleCommand(":")).AddSemicolon())))))
|
|
|
|
s.test("while read entry ; do case \"$$entry\" in include/c-client/* ) ${INSTALL_DATA} $$src $$dest ; esac ; done",
|
|
b.List().AddCommand(b.While(
|
|
b.List().AddCommand(b.SimpleCommand("read", "entry")).AddSemicolon(),
|
|
b.List().AddCommand(b.Case(
|
|
b.Token("\"$$entry\""),
|
|
b.CaseItem(
|
|
b.Words("include/c-client/*"),
|
|
b.List().AddCommand(b.SimpleCommand("${INSTALL_DATA}", "$$src", "$$dest")),
|
|
sepSemicolon))).AddSemicolon())))
|
|
|
|
s.test("command | while condition ; do case selector in pattern ) : ; esac ; done",
|
|
b.List().AddAndOr(b.AndOr(b.Pipeline(false,
|
|
b.SimpleCommand("command"),
|
|
b.While(
|
|
b.List().AddCommand(b.SimpleCommand("condition")).AddSemicolon(),
|
|
b.List().AddCommand(b.Case(
|
|
b.Token("selector"),
|
|
b.CaseItem(
|
|
b.Words("pattern"),
|
|
b.List().AddCommand(b.SimpleCommand(":")),
|
|
sepSemicolon))).AddSemicolon())))))
|
|
|
|
s.test("command1 \n command2 \n command3",
|
|
b.List().
|
|
AddCommand(b.SimpleCommand("command1")).
|
|
AddNewline().
|
|
AddCommand(b.SimpleCommand("command2")).
|
|
AddNewline().
|
|
AddCommand(b.SimpleCommand("command3")))
|
|
|
|
s.test("if condition; then action; else case selector in pattern) case-item-action ;; esac; fi",
|
|
b.List().AddCommand(b.If(
|
|
b.List().AddCommand(b.SimpleCommand("condition")).AddSemicolon(),
|
|
b.List().AddCommand(b.SimpleCommand("action")).AddSemicolon(),
|
|
b.List().AddCommand(b.Case(
|
|
b.Token("selector"),
|
|
b.CaseItem(
|
|
b.Words("pattern"),
|
|
b.List().AddCommand(b.SimpleCommand("case-item-action")), sepNone))).AddSemicolon())))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_list(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("echo1 && echo2",
|
|
b.List().AddAndOr(
|
|
b.AndOr(b.Pipeline(false, b.SimpleCommand("echo1"))).
|
|
Add("&&", b.Pipeline(false, b.SimpleCommand("echo2")))))
|
|
|
|
s.test("echo1 ; echo2",
|
|
b.List().
|
|
AddCommand(b.SimpleCommand("echo1")).
|
|
AddSemicolon().
|
|
AddCommand(b.SimpleCommand("echo2")))
|
|
|
|
s.test("echo1 ; echo2 &",
|
|
b.List().
|
|
AddCommand(b.SimpleCommand("echo1")).
|
|
AddSemicolon().
|
|
AddCommand(b.SimpleCommand("echo2")).
|
|
AddBackground())
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_and_or(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("echo1 | echo2",
|
|
b.List().AddAndOr(b.AndOr(b.Pipeline(false,
|
|
b.SimpleCommand("echo1"),
|
|
b.SimpleCommand("echo2")))))
|
|
|
|
s.test("echo1 | echo2 && echo3 | echo4",
|
|
b.List().AddAndOr(b.AndOr(
|
|
b.Pipeline(false,
|
|
b.SimpleCommand("echo1"),
|
|
b.SimpleCommand("echo2")),
|
|
).Add("&&",
|
|
b.Pipeline(false,
|
|
b.SimpleCommand("echo3"),
|
|
b.SimpleCommand("echo4")))))
|
|
|
|
s.test("echo1 | echo2 || ! echo3 | echo4",
|
|
b.List().AddAndOr(b.AndOr(
|
|
b.Pipeline(false,
|
|
b.SimpleCommand("echo1"),
|
|
b.SimpleCommand("echo2")),
|
|
).Add("||",
|
|
b.Pipeline(true,
|
|
b.SimpleCommand("echo3"),
|
|
b.SimpleCommand("echo4")))))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_pipeline(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("command1 | command2",
|
|
b.List().AddAndOr(b.AndOr(b.Pipeline(false,
|
|
b.SimpleCommand("command1"),
|
|
b.SimpleCommand("command2")))))
|
|
|
|
s.test("! command1 | command2",
|
|
b.List().AddAndOr(b.AndOr(b.Pipeline(true,
|
|
b.SimpleCommand("command1"),
|
|
b.SimpleCommand("command2")))))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_pipe_sequence(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("command1 | if true ; then : ; fi",
|
|
b.List().AddAndOr(b.AndOr(b.Pipeline(false,
|
|
b.SimpleCommand("command1"),
|
|
b.If(
|
|
b.List().AddCommand(b.SimpleCommand("true")).AddSemicolon(),
|
|
b.List().AddCommand(b.SimpleCommand(":")).AddSemicolon())))))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_command(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("simple_command",
|
|
b.List().AddAndOr(b.AndOr(b.Pipeline(false, b.SimpleCommand("simple_command")))))
|
|
|
|
s.test("while 1; do 2; done",
|
|
b.List().AddAndOr(b.AndOr(b.Pipeline(false,
|
|
b.While(
|
|
b.List().AddCommand(b.SimpleCommand("1")).AddSemicolon(),
|
|
b.List().AddCommand(b.SimpleCommand("2")).AddSemicolon())))))
|
|
|
|
s.test("while 1; do 2; done 1>&2",
|
|
b.List().AddAndOr(b.AndOr(b.Pipeline(false,
|
|
b.While(
|
|
b.List().AddCommand(b.SimpleCommand("1")).AddSemicolon(),
|
|
b.List().AddCommand(b.SimpleCommand("2")).AddSemicolon(),
|
|
b.Redirection(1, ">&", "2"))))))
|
|
|
|
s.test("func(){ echo hello;} 2>&1",
|
|
b.List().AddCommand(b.Function(
|
|
"func",
|
|
b.Brace(b.List().AddCommand(b.SimpleCommand("echo", "hello")).AddSemicolon()).Compound,
|
|
b.Redirection(2, ">&", "1"))))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_compound_command(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("{ brace ; }",
|
|
b.List().AddCommand(b.Brace(
|
|
b.List().AddCommand(b.SimpleCommand("brace")).AddSemicolon())))
|
|
|
|
s.test("( subshell )",
|
|
b.List().AddCommand(b.Subshell(
|
|
b.List().AddCommand(b.SimpleCommand("subshell")))))
|
|
|
|
s.test("for i in * ; do echo $i ; done",
|
|
b.List().AddCommand(b.For(
|
|
"i",
|
|
b.Words("*"),
|
|
b.List().AddCommand(b.SimpleCommand("echo", "$i")).AddSemicolon())))
|
|
|
|
s.test("case $i in esac",
|
|
b.List().AddCommand(b.Case(
|
|
b.Token("$i"))))
|
|
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_subshell(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
sub3 := b.Subshell(b.List().AddCommand(b.SimpleCommand("sub3")))
|
|
sub2 := b.Subshell(b.List().AddCommand(sub3).AddSemicolon().AddCommand(b.SimpleCommand("sub2")))
|
|
sub1 := b.Subshell(b.List().AddCommand(sub2).AddSemicolon().AddCommand(b.SimpleCommand("sub1")))
|
|
s.test("( ( ( sub3 ) ; sub2 ) ; sub1 )", b.List().AddCommand(sub1))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_compound_list(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("( \n echo )",
|
|
b.List().AddCommand(b.Subshell(
|
|
b.List().AddCommand(b.SimpleCommand("echo")))))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_term(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
_ = b
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_for_clause(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("for var do echo $var ; done",
|
|
b.List().AddCommand(b.For(
|
|
"var",
|
|
b.Words("\"$$@\""),
|
|
b.List().AddCommand(b.SimpleCommand("echo", "$var")).AddSemicolon())))
|
|
|
|
// Only linebreak is allowed, but not semicolon.
|
|
s.test("for var \n do echo $var ; done",
|
|
b.List().AddCommand(b.For(
|
|
"var",
|
|
b.Words("\"$$@\""),
|
|
b.List().AddCommand(b.SimpleCommand("echo", "$var")).AddSemicolon())))
|
|
|
|
s.test("for var in a b c ; do echo $var ; done",
|
|
b.List().AddCommand(b.For(
|
|
"var",
|
|
b.Words("a", "b", "c"),
|
|
b.List().AddCommand(b.SimpleCommand("echo", "$var")).AddSemicolon())))
|
|
|
|
s.test("for var \n \n \n in a b c ; do echo $var ; done",
|
|
b.List().AddCommand(b.For(
|
|
"var",
|
|
b.Words("a", "b", "c"),
|
|
b.List().AddCommand(b.SimpleCommand("echo", "$var")).AddSemicolon())))
|
|
|
|
s.test("for var in in esac ; do echo $var ; done",
|
|
b.List().AddCommand(b.For(
|
|
"var",
|
|
b.Words("in", "esac"),
|
|
b.List().AddCommand(b.SimpleCommand("echo", "$var")).AddSemicolon())))
|
|
|
|
// No semicolon necessary between the two “done”.
|
|
s.test("for i in 1; do for j in 1; do echo $$i$$j; done done",
|
|
b.List().AddCommand(b.For(
|
|
"i",
|
|
b.Words("1"),
|
|
b.List().AddCommand(b.For(
|
|
"j",
|
|
b.Words("1"),
|
|
b.List().AddCommand(b.SimpleCommand("echo", "$$i$$j")).AddSemicolon())))))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_case_clause(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("case $var in esac",
|
|
b.List().AddCommand(b.Case(b.Token("$var"))))
|
|
|
|
s.test("case selector in pattern) ;; pattern) esac",
|
|
b.List().AddCommand(b.Case(
|
|
b.Token("selector"),
|
|
b.CaseItem(b.Words("pattern"), b.List(), sepNone),
|
|
b.CaseItem(b.Words("pattern"), b.List(), sepNone))))
|
|
|
|
s.test("case $$i in *.c | *.h ) echo C ;; * ) echo Other ; esac",
|
|
b.List().AddCommand(b.Case(
|
|
b.Token("$$i"),
|
|
b.CaseItem(b.Words("*.c", "*.h"), b.List().AddCommand(b.SimpleCommand("echo", "C")), sepNone),
|
|
b.CaseItem(b.Words("*"), b.List().AddCommand(b.SimpleCommand("echo", "Other")), sepSemicolon))))
|
|
|
|
s.test("case $$i in *.c ) echo ; esac",
|
|
b.List().AddCommand(b.Case(
|
|
b.Token("$$i"),
|
|
b.CaseItem(b.Words("*.c"), b.List().AddCommand(b.SimpleCommand("echo")), sepSemicolon))))
|
|
|
|
s.test("case selector in pattern) case-item-action ; esac",
|
|
b.List().AddCommand(b.Case(
|
|
b.Token("selector"),
|
|
b.CaseItem(
|
|
b.Words("pattern"),
|
|
b.List().AddCommand(b.SimpleCommand("case-item-action")), sepSemicolon))))
|
|
|
|
s.test("case selector in pattern) case-item-action ;; esac",
|
|
b.List().AddCommand(b.Case(
|
|
b.Token("selector"),
|
|
b.CaseItem(
|
|
b.Words("pattern"),
|
|
b.List().AddCommand(b.SimpleCommand("case-item-action")), sepNone))))
|
|
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_if_clause(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test(
|
|
"if true ; then echo yes ; else echo no ; fi",
|
|
b.List().AddCommand(b.If(
|
|
b.List().AddCommand(b.SimpleCommand("true")).AddSemicolon(),
|
|
b.List().AddCommand(b.SimpleCommand("echo", "yes")).AddSemicolon(),
|
|
b.List().AddCommand(b.SimpleCommand("echo", "no")).AddSemicolon())))
|
|
|
|
// No semicolon necessary between the two “fi”.
|
|
s.test("if cond1; then if cond2; then action; fi fi",
|
|
b.List().AddCommand(b.If(
|
|
b.List().AddCommand(b.SimpleCommand("cond1")).AddSemicolon(),
|
|
b.List().AddCommand(b.If(
|
|
b.List().AddCommand(b.SimpleCommand("cond2")).AddSemicolon(),
|
|
b.List().AddCommand(b.SimpleCommand("action")).AddSemicolon())))))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_while_clause(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("while condition ; do action ; done",
|
|
b.List().AddCommand(b.While(
|
|
b.List().AddCommand(b.SimpleCommand("condition")).AddSemicolon(),
|
|
b.List().AddCommand(b.SimpleCommand("action")).AddSemicolon())))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_until_clause(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("until condition ; do action ; done",
|
|
b.List().AddCommand(b.Until(
|
|
b.List().AddCommand(b.SimpleCommand("condition")).AddSemicolon(),
|
|
b.List().AddCommand(b.SimpleCommand("action")).AddSemicolon())))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_function_definition(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
_ = b
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_brace_group(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
// No semicolon necessary after the closing brace.
|
|
s.test("if true; then { echo yes; } fi",
|
|
b.List().AddCommand(b.If(
|
|
b.List().AddCommand(b.SimpleCommand("true")).AddSemicolon(),
|
|
b.List().AddCommand(b.Brace(
|
|
b.List().AddCommand(b.SimpleCommand("echo", "yes")).AddSemicolon())))))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_simple_command(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test(
|
|
"echo hello, world",
|
|
b.List().AddCommand(b.SimpleCommand("echo", "hello,", "world")))
|
|
|
|
s.test("echo ${PKGNAME:Q}",
|
|
b.List().AddCommand(b.SimpleCommand("echo", "${PKGNAME:Q}")))
|
|
|
|
s.test("${ECHO} \"Double-quoted\" 'Single-quoted'",
|
|
b.List().AddCommand(b.SimpleCommand("${ECHO}", "\"Double-quoted\"", "'Single-quoted'")))
|
|
|
|
s.test("`cat plain` \"`cat double`\" '`cat single`'",
|
|
b.List().AddCommand(b.SimpleCommand("`cat plain`", "\"`cat double`\"", "'`cat single`'")))
|
|
|
|
s.test("`\"one word\"`",
|
|
b.List().AddCommand(b.SimpleCommand("`\"one word\"`")))
|
|
|
|
s.test("PAGES=\"`ls -1 | ${SED} -e 's,3qt$$,3,'`\"",
|
|
b.List().AddCommand(b.SimpleCommand("PAGES=\"`ls -1 | ${SED} -e 's,3qt$$,3,'`\"")))
|
|
|
|
s.test("var=Plain var=\"Dquot\" var='Squot' var=Plain\"Dquot\"'Squot'",
|
|
b.List().AddCommand(b.SimpleCommand("var=Plain", "var=\"Dquot\"", "var='Squot'", "var=Plain\"Dquot\"'Squot'")))
|
|
|
|
// RUN is a special Make variable since it ends with a semicolon;
|
|
// therefore it needs to be split off before passing the rest of
|
|
// the command to the shell command parser.
|
|
s.test("${RUN} subdir=\"`unzip -c \"$$e\" install.rdf | awk '/re/ { print \"hello\" }'`\"",
|
|
b.List().AddCommand(b.SimpleCommand("${RUN}", "subdir=\"`unzip -c \"$$e\" install.rdf | awk '/re/ { print \"hello\" }'`\"")))
|
|
|
|
s.test("PATH=/nonexistent env PATH=${PATH:Q} true",
|
|
b.List().AddCommand(b.SimpleCommand("PATH=/nonexistent", "env", "PATH=${PATH:Q}", "true")))
|
|
|
|
s.test("{OpenGrok args",
|
|
b.List().AddCommand(b.SimpleCommand("{OpenGrok", "args")))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_io_redirect(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
s.test("echo >> ${PLIST_SRC}",
|
|
b.List().AddCommand(b.SimpleCommand("echo", ">>${PLIST_SRC}")))
|
|
|
|
s.test("echo >> ${PLIST_SRC}",
|
|
b.List().AddCommand(b.SimpleCommand("echo", ">>${PLIST_SRC}")))
|
|
|
|
s.test("echo 1>output 2>>append 3>|clobber 4>&5 6<input >>append",
|
|
b.List().AddCommand(&MkShCommand{Simple: &MkShSimpleCommand{
|
|
Assignments: nil,
|
|
Name: b.Token("echo"),
|
|
Args: nil,
|
|
Redirections: []*MkShRedirection{
|
|
{1, ">", b.Token("output")},
|
|
{2, ">>", b.Token("append")},
|
|
{3, ">|", b.Token("clobber")},
|
|
{4, ">&", b.Token("5")},
|
|
{6, "<", b.Token("input")},
|
|
{-1, ">>", b.Token("append")}}}}))
|
|
|
|
s.test("echo 1> output 2>> append 3>| clobber 4>& 5 6< input >> append",
|
|
b.List().AddCommand(&MkShCommand{Simple: &MkShSimpleCommand{
|
|
Assignments: nil,
|
|
Name: b.Token("echo"),
|
|
Args: nil,
|
|
Redirections: []*MkShRedirection{
|
|
{1, ">", b.Token("output")},
|
|
{2, ">>", b.Token("append")},
|
|
{3, ">|", b.Token("clobber")},
|
|
{4, ">&", b.Token("5")},
|
|
{6, "<", b.Token("input")},
|
|
{-1, ">>", b.Token("append")}}}}))
|
|
|
|
s.test("${MAKE} print-summary-data 2>&1 > /dev/stderr",
|
|
b.List().AddCommand(&MkShCommand{Simple: &MkShSimpleCommand{
|
|
Assignments: nil,
|
|
Name: b.Token("${MAKE}"),
|
|
Args: []*ShToken{b.Token("print-summary-data")},
|
|
Redirections: []*MkShRedirection{
|
|
{2, ">&", b.Token("1")},
|
|
{-1, ">", b.Token("/dev/stderr")}}}}))
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellParser_io_here(c *check.C) {
|
|
b := s.init(c)
|
|
|
|
_ = b
|
|
}
|
|
|
|
func (s *ShSuite) init(c *check.C) *MkShBuilder {
|
|
s.c = c
|
|
return NewMkShBuilder()
|
|
}
|
|
|
|
func (s *ShSuite) test(program string, expected *MkShList) {
|
|
tokens, rest := splitIntoShellTokens(dummyLine, program)
|
|
s.c.Check(rest, equals, "")
|
|
lexer := &ShellLexer{
|
|
current: "",
|
|
remaining: tokens,
|
|
atCommandStart: true,
|
|
error: ""}
|
|
parser := ­yParserImpl{}
|
|
|
|
succeeded := parser.Parse(lexer)
|
|
|
|
c := s.c
|
|
|
|
if ok1, ok2 := c.Check(succeeded, equals, 0), c.Check(lexer.error, equals, ""); ok1 && ok2 {
|
|
if !c.Check(lexer.result, deepEquals, expected) {
|
|
actualJSON, actualErr := json.MarshalIndent(lexer.result, "", " ")
|
|
expectedJSON, expectedErr := json.MarshalIndent(expected, "", " ")
|
|
if c.Check(actualErr, check.IsNil) && c.Check(expectedErr, check.IsNil) {
|
|
c.Check(string(actualJSON), deepEquals, string(expectedJSON))
|
|
}
|
|
}
|
|
} else {
|
|
c.Check(lexer.remaining, deepEquals, []string{})
|
|
}
|
|
}
|
|
|
|
func (s *ShSuite) Test_ShellLexer_Lex__redirects(c *check.C) {
|
|
tokens, rest := splitIntoShellTokens(dummyLine, "${MAKE} print-summary-data 2>&1 > /dev/stderr")
|
|
|
|
c.Check(tokens, deepEquals, []string{"${MAKE}", "print-summary-data", "2>&", "1", ">", "/dev/stderr"})
|
|
c.Check(rest, equals, "")
|
|
|
|
lexer := NewShellLexer(tokens, rest)
|
|
var llval shyySymType
|
|
|
|
c.Check(lexer.Lex(&llval), equals, tkWORD)
|
|
c.Check(llval.Word.MkText, equals, "${MAKE}")
|
|
|
|
c.Check(lexer.Lex(&llval), equals, tkWORD)
|
|
c.Check(llval.Word.MkText, equals, "print-summary-data")
|
|
|
|
c.Check(lexer.Lex(&llval), equals, tkIO_NUMBER)
|
|
c.Check(llval.IONum, equals, 2)
|
|
|
|
c.Check(lexer.Lex(&llval), equals, tkGTAND)
|
|
|
|
c.Check(lexer.Lex(&llval), equals, tkWORD)
|
|
c.Check(llval.Word.MkText, equals, "1")
|
|
|
|
c.Check(lexer.Lex(&llval), equals, tkGT)
|
|
|
|
c.Check(lexer.Lex(&llval), equals, tkWORD)
|
|
c.Check(llval.Word.MkText, equals, "/dev/stderr")
|
|
|
|
c.Check(lexer.Lex(&llval), equals, 0)
|
|
}
|
|
|
|
type MkShBuilder struct {
|
|
}
|
|
|
|
func NewMkShBuilder() *MkShBuilder {
|
|
return &MkShBuilder{}
|
|
}
|
|
|
|
func (b *MkShBuilder) List() *MkShList {
|
|
return NewMkShList()
|
|
}
|
|
|
|
func (b *MkShBuilder) AndOr(pipeline *MkShPipeline) *MkShAndOr {
|
|
return NewMkShAndOr(pipeline)
|
|
}
|
|
|
|
func (b *MkShBuilder) Pipeline(negated bool, cmds ...*MkShCommand) *MkShPipeline {
|
|
return NewMkShPipeline(negated, cmds...)
|
|
}
|
|
|
|
func (b *MkShBuilder) SimpleCommand(words ...string) *MkShCommand {
|
|
cmd := &MkShSimpleCommand{}
|
|
assignments := true
|
|
for _, word := range words {
|
|
if assignments && matches(word, `^\w+=`) {
|
|
cmd.Assignments = append(cmd.Assignments, b.Token(word))
|
|
} else if m, fdstr, op, rest := match3(word, `^(\d*)(<<-|<<|<&|>>|>&|>\||<|>)(.*)$`); m {
|
|
fd, err := strconv.Atoi(fdstr)
|
|
if err != nil {
|
|
fd = -1
|
|
}
|
|
cmd.Redirections = append(cmd.Redirections, b.Redirection(fd, op, rest))
|
|
} else {
|
|
assignments = false
|
|
if cmd.Name == nil {
|
|
cmd.Name = b.Token(word)
|
|
} else {
|
|
cmd.Args = append(cmd.Args, b.Token(word))
|
|
}
|
|
}
|
|
}
|
|
return &MkShCommand{Simple: cmd}
|
|
}
|
|
|
|
func (b *MkShBuilder) If(condActionElse ...*MkShList) *MkShCommand {
|
|
ifclause := &MkShIfClause{}
|
|
for i, part := range condActionElse {
|
|
if i%2 == 0 && i != len(condActionElse)-1 {
|
|
ifclause.Conds = append(ifclause.Conds, part)
|
|
} else if i%2 == 1 {
|
|
ifclause.Actions = append(ifclause.Actions, part)
|
|
} else {
|
|
ifclause.Else = part
|
|
}
|
|
}
|
|
return &MkShCommand{Compound: &MkShCompoundCommand{If: ifclause}}
|
|
}
|
|
|
|
func (b *MkShBuilder) For(varname string, items []*ShToken, action *MkShList) *MkShCommand {
|
|
return &MkShCommand{Compound: &MkShCompoundCommand{For: &MkShForClause{varname, items, action}}}
|
|
}
|
|
|
|
func (b *MkShBuilder) Case(selector *ShToken, items ...*MkShCaseItem) *MkShCommand {
|
|
return &MkShCommand{Compound: &MkShCompoundCommand{Case: &MkShCaseClause{selector, items}}}
|
|
}
|
|
|
|
func (b *MkShBuilder) CaseItem(patterns []*ShToken, action *MkShList, separator MkShSeparator) *MkShCaseItem {
|
|
return &MkShCaseItem{patterns, action, separator}
|
|
}
|
|
|
|
func (b *MkShBuilder) While(cond, action *MkShList, redirects ...*MkShRedirection) *MkShCommand {
|
|
return &MkShCommand{
|
|
Compound: &MkShCompoundCommand{
|
|
Loop: &MkShLoopClause{cond, action, false}},
|
|
Redirects: redirects}
|
|
}
|
|
|
|
func (b *MkShBuilder) Until(cond, action *MkShList, redirects ...*MkShRedirection) *MkShCommand {
|
|
return &MkShCommand{
|
|
Compound: &MkShCompoundCommand{
|
|
Loop: &MkShLoopClause{cond, action, true}},
|
|
Redirects: redirects}
|
|
}
|
|
|
|
func (b *MkShBuilder) Function(name string, body *MkShCompoundCommand, redirects ...*MkShRedirection) *MkShCommand {
|
|
return &MkShCommand{
|
|
FuncDef: &MkShFunctionDefinition{name, body},
|
|
Redirects: redirects}
|
|
}
|
|
|
|
func (b *MkShBuilder) Brace(list *MkShList) *MkShCommand {
|
|
return &MkShCommand{Compound: &MkShCompoundCommand{Brace: list}}
|
|
}
|
|
|
|
func (b *MkShBuilder) Subshell(list *MkShList) *MkShCommand {
|
|
return &MkShCommand{Compound: &MkShCompoundCommand{Subshell: list}}
|
|
}
|
|
|
|
func (b *MkShBuilder) Token(mktext string) *ShToken {
|
|
tokenizer := NewShTokenizer(dummyLine, mktext, false)
|
|
token := tokenizer.ShToken()
|
|
return token
|
|
}
|
|
|
|
func (b *MkShBuilder) Words(words ...string) []*ShToken {
|
|
tokens := make([]*ShToken, len(words))
|
|
for i, word := range words {
|
|
tokens[i] = b.Token(word)
|
|
}
|
|
return tokens
|
|
}
|
|
|
|
func (b *MkShBuilder) Redirection(fd int, op string, target string) *MkShRedirection {
|
|
return &MkShRedirection{fd, op, b.Token(target)}
|
|
}
|