1b35a1733607b07f36c3617932b67debb375a15c5Daniel Dunbarfrom __future__ import absolute_import
2be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbarimport itertools
3be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
4128ce319ec47c46dc7da16aa3a75185899878745Daniel Dunbarimport lit.util
5b35a1733607b07f36c3617932b67debb375a15c5Daniel Dunbarfrom lit.ShCommands import Command, Pipeline, Seq
6be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
7be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbarclass ShLexer:
8be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def __init__(self, data, win32Escapes = False):
9be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.data = data
10be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.pos = 0
11be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.end = len(data)
12be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.win32Escapes = win32Escapes
13be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
14be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def eat(self):
15be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        c = self.data[self.pos]
16be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.pos += 1
17be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return c
18be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
19be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def look(self):
20be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return self.data[self.pos]
21be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
22be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def maybe_eat(self, c):
23be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        """
24be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        maybe_eat(c) - Consume the character c if it is the next character,
25be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        returning True if a character was consumed. """
26be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if self.data[self.pos] == c:
27be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            self.pos += 1
28be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            return True
29be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return False
30be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
31be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def lex_arg_fast(self, c):
32be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        # Get the leading whitespace free section.
33be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        chunk = self.data[self.pos - 1:].split(None, 1)[0]
34be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
35be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        # If it has special characters, the fast path failed.
36be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if ('|' in chunk or '&' in chunk or
37be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            '<' in chunk or '>' in chunk or
38be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            "'" in chunk or '"' in chunk or
3963a08e09ae301241d0bdd0618bb0533a1d303cf5Daniel Dunbar            ';' in chunk or '\\' in chunk):
40be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            return None
41be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
42be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.pos = self.pos - 1 + len(chunk)
43be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return chunk
44be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
45be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def lex_arg_slow(self, c):
46be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if c in "'\"":
47be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            str = self.lex_arg_quoted(c)
48be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        else:
49be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            str = c
50be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        while self.pos != self.end:
51be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            c = self.look()
5263a08e09ae301241d0bdd0618bb0533a1d303cf5Daniel Dunbar            if c.isspace() or c in "|&;":
53be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                break
54be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            elif c in '><':
55be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                # This is an annoying case; we treat '2>' as a single token so
56be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                # we don't have to track whitespace tokens.
57be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
58be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                # If the parse string isn't an integer, do the usual thing.
59be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                if not str.isdigit():
60be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                    break
61be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
62be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                # Otherwise, lex the operator and convert to a redirection
63be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                # token.
64be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                num = int(str)
65be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                tok = self.lex_one_token()
66be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                assert isinstance(tok, tuple) and len(tok) == 1
67be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                return (tok[0], num)
68be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            elif c == '"':
69be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                self.eat()
703f451cafb5ba3a022c8e05cf2d054835f807dd11Daniel Dunbar                str += self.lex_arg_quoted('"')
71bd4fa2efd373c46dc14b87744b908f16f539c836Daniel Dunbar            elif c == "'":
72bd4fa2efd373c46dc14b87744b908f16f539c836Daniel Dunbar                self.eat()
73bd4fa2efd373c46dc14b87744b908f16f539c836Daniel Dunbar                str += self.lex_arg_quoted("'")
74be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            elif not self.win32Escapes and c == '\\':
75be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                # Outside of a string, '\\' escapes everything.
76be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                self.eat()
77be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                if self.pos == self.end:
78128ce319ec47c46dc7da16aa3a75185899878745Daniel Dunbar                    lit.util.warning(
79b35a1733607b07f36c3617932b67debb375a15c5Daniel Dunbar                        "escape at end of quoted argument in: %r" % self.data)
80be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                    return str
81be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                str += self.eat()
82be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            else:
83be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                str += self.eat()
84be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return str
85be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
86be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def lex_arg_quoted(self, delim):
87be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        str = ''
88be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        while self.pos != self.end:
89be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            c = self.eat()
90be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if c == delim:
91be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                return str
92be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            elif c == '\\' and delim == '"':
93be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                # Inside a '"' quoted string, '\\' only escapes the quote
94be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                # character and backslash, otherwise it is preserved.
95be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                if self.pos == self.end:
96128ce319ec47c46dc7da16aa3a75185899878745Daniel Dunbar                    lit.util.warning(
97b35a1733607b07f36c3617932b67debb375a15c5Daniel Dunbar                        "escape at end of quoted argument in: %r" % self.data)
98be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                    return str
99be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                c = self.eat()
100be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                if c == '"': #
101be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                    str += '"'
102be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                elif c == '\\':
103be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                    str += '\\'
104be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                else:
105be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                    str += '\\' + c
106be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            else:
107be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                str += c
108128ce319ec47c46dc7da16aa3a75185899878745Daniel Dunbar        lit.util.warning("missing quote character in %r" % self.data)
109be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return str
110be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
111be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def lex_arg_checked(self, c):
112be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        pos = self.pos
113be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        res = self.lex_arg_fast(c)
114be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        end = self.pos
115be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
116be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.pos = pos
117be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        reference = self.lex_arg_slow(c)
118be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if res is not None:
119be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if res != reference:
12026e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar                raise ValueError("Fast path failure: %r != %r" % (
12126e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar                        res, reference))
122be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if self.pos != end:
12326e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar                raise ValueError("Fast path failure: %r != %r" % (
12426e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar                        self.pos, end))
125be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return reference
126be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
127be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def lex_arg(self, c):
128be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return self.lex_arg_fast(c) or self.lex_arg_slow(c)
129be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
130be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def lex_one_token(self):
131be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        """
132be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        lex_one_token - Lex a single 'sh' token. """
133be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
134be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        c = self.eat()
13552d4de971fe523fe43d6f868331a14ed1743f35aNAKAMURA Takumi        if c == ';':
136be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            return (c,)
137be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if c == '|':
138be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if self.maybe_eat('|'):
139be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                return ('||',)
140be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            return (c,)
141be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if c == '&':
142be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if self.maybe_eat('&'):
143be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                return ('&&',)
144be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if self.maybe_eat('>'):
145be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                return ('&>',)
146be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            return (c,)
147be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if c == '>':
148be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if self.maybe_eat('&'):
149be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                return ('>&',)
150be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if self.maybe_eat('>'):
151be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                return ('>>',)
152be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            return (c,)
153be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if c == '<':
154be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if self.maybe_eat('&'):
155be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                return ('<&',)
156be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if self.maybe_eat('>'):
157be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                return ('<<',)
158be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            return (c,)
159be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
160be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return self.lex_arg(c)
161be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
162be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def lex(self):
163be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        while self.pos != self.end:
164be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if self.look().isspace():
165be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                self.eat()
166be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            else:
167be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                yield self.lex_one_token()
168be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
169be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar###
170be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
171be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbarclass ShParser:
172c1bb2d432501dabdfcb1e78eccfb7377664c4d14Rafael Espindola    def __init__(self, data, win32Escapes = False, pipefail = False):
173be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.data = data
174c1bb2d432501dabdfcb1e78eccfb7377664c4d14Rafael Espindola        self.pipefail = pipefail
175be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.tokens = ShLexer(data, win32Escapes = win32Escapes).lex()
176be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
177be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def lex(self):
1782e10ff28f24a26829ae9a43fc49b91eb974489efDaniel Dunbar        for item in self.tokens:
1792e10ff28f24a26829ae9a43fc49b91eb974489efDaniel Dunbar            return item
1802e10ff28f24a26829ae9a43fc49b91eb974489efDaniel Dunbar        return None
181be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
182be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def look(self):
1832e10ff28f24a26829ae9a43fc49b91eb974489efDaniel Dunbar        token = self.lex()
1842e10ff28f24a26829ae9a43fc49b91eb974489efDaniel Dunbar        if token is not None:
1852e10ff28f24a26829ae9a43fc49b91eb974489efDaniel Dunbar            self.tokens = itertools.chain([token], self.tokens)
1862e10ff28f24a26829ae9a43fc49b91eb974489efDaniel Dunbar        return token
187be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
188be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def parse_command(self):
189be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        tok = self.lex()
190be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if not tok:
19126e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar            raise ValueError("empty command!")
192be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if isinstance(tok, tuple):
19326e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar            raise ValueError("syntax error near unexpected token %r" % tok[0])
194be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
195be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        args = [tok]
196be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        redirects = []
197be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        while 1:
198be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            tok = self.look()
199be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
200be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            # EOF?
201be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if tok is None:
202be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                break
203be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
204be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            # If this is an argument, just add it to the current command.
205be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if isinstance(tok, str):
206be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                args.append(self.lex())
207be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                continue
208be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
209be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            # Otherwise see if it is a terminator.
210be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            assert isinstance(tok, tuple)
2111f7ebddd5f6f6787023a0b7b2cd3dd4e80e10447Chandler Carruth            if tok[0] in ('|',';','&','||','&&'):
212be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                break
213be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
214be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            # Otherwise it must be a redirection.
215be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            op = self.lex()
216be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            arg = self.lex()
217be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if not arg:
21826e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar                raise ValueError("syntax error near token %r" % op[0])
219be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            redirects.append((op, arg))
220be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
221be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return Command(args, redirects)
222be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
223be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def parse_pipeline(self):
224be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        negate = False
225be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
226be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        commands = [self.parse_command()]
2271f7ebddd5f6f6787023a0b7b2cd3dd4e80e10447Chandler Carruth        while self.look() == ('|',):
2281f7ebddd5f6f6787023a0b7b2cd3dd4e80e10447Chandler Carruth            self.lex()
2291f7ebddd5f6f6787023a0b7b2cd3dd4e80e10447Chandler Carruth            commands.append(self.parse_command())
230c1bb2d432501dabdfcb1e78eccfb7377664c4d14Rafael Espindola        return Pipeline(commands, negate, self.pipefail)
231be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
232be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def parse(self):
233be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        lhs = self.parse_pipeline()
234be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
235be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        while self.look():
236be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            operator = self.lex()
237be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            assert isinstance(operator, tuple) and len(operator) == 1
238be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
239be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if not self.look():
24026e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar                raise ValueError(
24126e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar                    "missing argument to operator %r" % operator[0])
242be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
243be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            # FIXME: Operator precedence!!
244be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            lhs = Seq(lhs, operator[0], self.parse_pipeline())
245be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
246be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return lhs
247be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
248be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar###
249be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
250be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbarimport unittest
251be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
252be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbarclass TestShLexer(unittest.TestCase):
253be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def lex(self, str, *args, **kwargs):
254be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return list(ShLexer(str, *args, **kwargs).lex())
255be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
256be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def test_basic(self):
25763a08e09ae301241d0bdd0618bb0533a1d303cf5Daniel Dunbar        self.assertEqual(self.lex('a|b>c&d<e;f'),
258be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         ['a', ('|',), 'b', ('>',), 'c', ('&',), 'd',
25963a08e09ae301241d0bdd0618bb0533a1d303cf5Daniel Dunbar                          ('<',), 'e', (';',), 'f'])
260be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
261be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def test_redirection_tokens(self):
262be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.lex('a2>c'),
263be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         ['a2', ('>',), 'c'])
264be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.lex('a 2>c'),
265be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         ['a', ('>',2), 'c'])
266be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
267be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def test_quoting(self):
268be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.lex(""" 'a' """),
269be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         ['a'])
270be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.lex(""" "hello\\"world" """),
271be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         ['hello"world'])
272be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.lex(""" "hello\\'world" """),
273be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         ["hello\\'world"])
274be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.lex(""" "hello\\\\world" """),
275be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         ["hello\\world"])
276be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.lex(""" he"llo wo"rld """),
277be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         ["hello world"])
278be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.lex(""" a\\ b a\\\\b """),
279be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         ["a b", "a\\b"])
280be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.lex(""" "" "" """),
281be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         ["", ""])
282be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.lex(""" a\\ b """, win32Escapes = True),
283be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         ['a\\', 'b'])
284be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
285be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbarclass TestShParse(unittest.TestCase):
286be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def parse(self, str):
287be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return ShParser(str).parse()
288be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
289be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def test_basic(self):
290be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('echo hello'),
291be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Pipeline([Command(['echo', 'hello'], [])], False))
292be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('echo ""'),
293be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Pipeline([Command(['echo', ''], [])], False))
294bd4fa2efd373c46dc14b87744b908f16f539c836Daniel Dunbar        self.assertEqual(self.parse("""echo -DFOO='a'"""),
295bd4fa2efd373c46dc14b87744b908f16f539c836Daniel Dunbar                         Pipeline([Command(['echo', '-DFOO=a'], [])], False))
296bd4fa2efd373c46dc14b87744b908f16f539c836Daniel Dunbar        self.assertEqual(self.parse('echo -DFOO="a"'),
297bd4fa2efd373c46dc14b87744b908f16f539c836Daniel Dunbar                         Pipeline([Command(['echo', '-DFOO=a'], [])], False))
298be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
299be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def test_redirection(self):
300be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('echo hello > c'),
301be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Pipeline([Command(['echo', 'hello'],
302be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                                           [((('>'),), 'c')])], False))
303be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('echo hello > c >> d'),
304be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Pipeline([Command(['echo', 'hello'], [(('>',), 'c'),
305be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                                                     (('>>',), 'd')])], False))
306be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('a 2>&1'),
307be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Pipeline([Command(['a'], [(('>&',2), '1')])], False))
308be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
309be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def test_pipeline(self):
310be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('a | b'),
311be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Pipeline([Command(['a'], []),
312be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                                   Command(['b'], [])],
313be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                                  False))
314be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
315be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('a | b | c'),
316be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Pipeline([Command(['a'], []),
317be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                                   Command(['b'], []),
318be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                                   Command(['c'], [])],
319be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                                  False))
320be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
321be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def test_list(self):
322be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('a ; b'),
323be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Seq(Pipeline([Command(['a'], [])], False),
324be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                             ';',
325be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                             Pipeline([Command(['b'], [])], False)))
326be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
327be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('a & b'),
328be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Seq(Pipeline([Command(['a'], [])], False),
329be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                             '&',
330be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                             Pipeline([Command(['b'], [])], False)))
331be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
332be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('a && b'),
333be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Seq(Pipeline([Command(['a'], [])], False),
334be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                             '&&',
335be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                             Pipeline([Command(['b'], [])], False)))
336be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
337be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('a || b'),
338be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Seq(Pipeline([Command(['a'], [])], False),
339be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                             '||',
340be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                             Pipeline([Command(['b'], [])], False)))
341be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
342be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.assertEqual(self.parse('a && b || c'),
343be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                         Seq(Seq(Pipeline([Command(['a'], [])], False),
344be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                                 '&&',
345be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                                 Pipeline([Command(['b'], [])], False)),
346be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                             '||',
347be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                             Pipeline([Command(['c'], [])], False)))
348be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
34963a08e09ae301241d0bdd0618bb0533a1d303cf5Daniel Dunbar        self.assertEqual(self.parse('a; b'),
35063a08e09ae301241d0bdd0618bb0533a1d303cf5Daniel Dunbar                         Seq(Pipeline([Command(['a'], [])], False),
35163a08e09ae301241d0bdd0618bb0533a1d303cf5Daniel Dunbar                             ';',
35263a08e09ae301241d0bdd0618bb0533a1d303cf5Daniel Dunbar                             Pipeline([Command(['b'], [])], False)))
35363a08e09ae301241d0bdd0618bb0533a1d303cf5Daniel Dunbar
354be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbarif __name__ == '__main__':
355be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    unittest.main()
356