123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- from __future__ import absolute_import
- import itertools
- import lit.util
- from lit.ShCommands import Command, Pipeline, Seq
- class ShLexer:
- def __init__(self, data, win32Escapes = False):
- self.data = data
- self.pos = 0
- self.end = len(data)
- self.win32Escapes = win32Escapes
- def eat(self):
- c = self.data[self.pos]
- self.pos += 1
- return c
- def look(self):
- return self.data[self.pos]
- def maybe_eat(self, c):
- """
- maybe_eat(c) - Consume the character c if it is the next character,
- returning True if a character was consumed. """
- if self.data[self.pos] == c:
- self.pos += 1
- return True
- return False
- def lex_arg_fast(self, c):
- # Get the leading whitespace free section.
- chunk = self.data[self.pos - 1:].split(None, 1)[0]
-
- # If it has special characters, the fast path failed.
- if ('|' in chunk or '&' in chunk or
- '<' in chunk or '>' in chunk or
- "'" in chunk or '"' in chunk or
- ';' in chunk or '\\' in chunk):
- return None
-
- self.pos = self.pos - 1 + len(chunk)
- return chunk
-
- def lex_arg_slow(self, c):
- if c in "'\"":
- str = self.lex_arg_quoted(c)
- else:
- str = c
- while self.pos != self.end:
- c = self.look()
- if c.isspace() or c in "|&;":
- break
- elif c in '><':
- # This is an annoying case; we treat '2>' as a single token so
- # we don't have to track whitespace tokens.
- # If the parse string isn't an integer, do the usual thing.
- if not str.isdigit():
- break
- # Otherwise, lex the operator and convert to a redirection
- # token.
- num = int(str)
- tok = self.lex_one_token()
- assert isinstance(tok, tuple) and len(tok) == 1
- return (tok[0], num)
- elif c == '"':
- self.eat()
- str += self.lex_arg_quoted('"')
- elif c == "'":
- self.eat()
- str += self.lex_arg_quoted("'")
- elif not self.win32Escapes and c == '\\':
- # Outside of a string, '\\' escapes everything.
- self.eat()
- if self.pos == self.end:
- lit.util.warning(
- "escape at end of quoted argument in: %r" % self.data)
- return str
- str += self.eat()
- else:
- str += self.eat()
- return str
- def lex_arg_quoted(self, delim):
- str = ''
- while self.pos != self.end:
- c = self.eat()
- if c == delim:
- return str
- elif c == '\\' and delim == '"':
- # Inside a '"' quoted string, '\\' only escapes the quote
- # character and backslash, otherwise it is preserved.
- if self.pos == self.end:
- lit.util.warning(
- "escape at end of quoted argument in: %r" % self.data)
- return str
- c = self.eat()
- if c == '"': #
- str += '"'
- elif c == '\\':
- str += '\\'
- else:
- str += '\\' + c
- else:
- str += c
- lit.util.warning("missing quote character in %r" % self.data)
- return str
-
- def lex_arg_checked(self, c):
- pos = self.pos
- res = self.lex_arg_fast(c)
- end = self.pos
- self.pos = pos
- reference = self.lex_arg_slow(c)
- if res is not None:
- if res != reference:
- raise ValueError("Fast path failure: %r != %r" % (
- res, reference))
- if self.pos != end:
- raise ValueError("Fast path failure: %r != %r" % (
- self.pos, end))
- return reference
-
- def lex_arg(self, c):
- return self.lex_arg_fast(c) or self.lex_arg_slow(c)
-
- def lex_one_token(self):
- """
- lex_one_token - Lex a single 'sh' token. """
- c = self.eat()
- if c == ';':
- return (c,)
- if c == '|':
- if self.maybe_eat('|'):
- return ('||',)
- return (c,)
- if c == '&':
- if self.maybe_eat('&'):
- return ('&&',)
- if self.maybe_eat('>'):
- return ('&>',)
- return (c,)
- if c == '>':
- if self.maybe_eat('&'):
- return ('>&',)
- if self.maybe_eat('>'):
- return ('>>',)
- return (c,)
- if c == '<':
- if self.maybe_eat('&'):
- return ('<&',)
- if self.maybe_eat('>'):
- return ('<<',)
- return (c,)
- return self.lex_arg(c)
- def lex(self):
- while self.pos != self.end:
- if self.look().isspace():
- self.eat()
- else:
- yield self.lex_one_token()
- ###
-
- class ShParser:
- def __init__(self, data, win32Escapes = False, pipefail = False):
- self.data = data
- self.pipefail = pipefail
- self.tokens = ShLexer(data, win32Escapes = win32Escapes).lex()
-
- def lex(self):
- for item in self.tokens:
- return item
- return None
-
- def look(self):
- token = self.lex()
- if token is not None:
- self.tokens = itertools.chain([token], self.tokens)
- return token
-
- def parse_command(self):
- tok = self.lex()
- if not tok:
- raise ValueError("empty command!")
- if isinstance(tok, tuple):
- raise ValueError("syntax error near unexpected token %r" % tok[0])
-
- args = [tok]
- redirects = []
- while 1:
- tok = self.look()
- # EOF?
- if tok is None:
- break
- # If this is an argument, just add it to the current command.
- if isinstance(tok, str):
- args.append(self.lex())
- continue
- # Otherwise see if it is a terminator.
- assert isinstance(tok, tuple)
- if tok[0] in ('|',';','&','||','&&'):
- break
-
- # Otherwise it must be a redirection.
- op = self.lex()
- arg = self.lex()
- if not arg:
- raise ValueError("syntax error near token %r" % op[0])
- redirects.append((op, arg))
- return Command(args, redirects)
- def parse_pipeline(self):
- negate = False
- commands = [self.parse_command()]
- while self.look() == ('|',):
- self.lex()
- commands.append(self.parse_command())
- return Pipeline(commands, negate, self.pipefail)
-
- def parse(self):
- lhs = self.parse_pipeline()
- while self.look():
- operator = self.lex()
- assert isinstance(operator, tuple) and len(operator) == 1
- if not self.look():
- raise ValueError(
- "missing argument to operator %r" % operator[0])
-
- # FIXME: Operator precedence!!
- lhs = Seq(lhs, operator[0], self.parse_pipeline())
- return lhs
|