Example #1
0
def _TwoArgs(w_parser):
  """Returns an expression tree to be evaluated."""
  w0 = w_parser.Read()
  w1 = w_parser.Read()
  if w0.s == '!':
    return bool_expr.LogicalNot(bool_expr.WordTest(w1))
  unary_id = TEST_UNARY_LOOKUP.get(w0.s)
  if unary_id is None:
    # TODO:
    # - separate lookup by unary
    p_die('Expected unary operator, got %r (2 args)', w0.s, word=w0)
  return bool_expr.Unary(unary_id, w1)
    def _ParseFormatStr(self):
        # type: () -> printf_part_t
        self._Next(lex_mode_e.PrintfPercent)  # move past %

        part = printf_part.Percent()
        while self.token_type in (Id.Format_Flag, Id.Format_Zero):
            # space and + could be implemented
            flag = self.cur_token.val
            if flag in '# +':
                p_die("osh printf doesn't support the %r flag",
                      flag,
                      token=self.cur_token)

            part.flags.append(self.cur_token)
            self._Next(lex_mode_e.PrintfPercent)

        if self.token_type in (Id.Format_Num, Id.Format_Star):
            part.width = self.cur_token
            self._Next(lex_mode_e.PrintfPercent)

        if self.token_type == Id.Format_Dot:
            part.precision = self.cur_token
            self._Next(lex_mode_e.PrintfPercent)  # past dot
            if self.token_type in (Id.Format_Num, Id.Format_Star,
                                   Id.Format_Zero):
                part.precision = self.cur_token
                self._Next(lex_mode_e.PrintfPercent)

        if self.token_type in (Id.Format_Type, Id.Format_Time):
            part.type = self.cur_token

            # ADDITIONAL VALIDATION outside the "grammar".
            if part.type.val in 'eEfFgG':
                p_die("osh printf doesn't support floating point",
                      token=part.type)
            # These two could be implemented.  %c needs utf-8 decoding.
            if part.type.val == 'c':
                p_die("osh printf doesn't support single characters (bytes)",
                      token=part.type)

        else:
            if self.cur_token.val:
                msg = 'Invalid printf format character'
            else:  # for printf '%'
                msg = 'Expected a printf format character'
            p_die(msg, token=self.cur_token)

        # Do this check AFTER the floating point checks
        if part.precision and part.type.val[-1] not in 'fsT':
            p_die("precision can't be specified when here",
                  token=part.precision)

        return part
Example #3
0
    def ParseForBuiltin(self):
        # type: () -> bool_expr_t
        """For test builtin."""
        self._Next()

        node = self.ParseExpr()
        if self.op_id != Id.Eof_Real:
            p_die('Unexpected trailing word %s',
                  word.Pretty(self.cur_word),
                  word=self.cur_word)

        return node
Example #4
0
    def ParseOilArgList(self, lexer, out):
        # type: (Lexer, arg_list) -> token
        if self.parsing_expr:
            p_die("TODO: can't be nested")

        pnode, last_token = self._ParseOil(lexer, grammar_nt.oil_arglist)

        if 0:
            self.p_printer.Print(pnode)

        self.tr.ArgList(pnode, out)
        return last_token
Example #5
0
    def _ReadExtGlobPart(self):
        # type: () -> word_part__ExtGlobPart
        """
    Grammar:
      Item         = CompoundWord | EPSILON  # important: @(foo|) is allowed
      LEFT         = '@(' | '*(' | '+(' | '?(' | '!('
      RIGHT        = ')'
      ExtGlob      = LEFT (Item '|')* Item RIGHT  # ITEM may be empty
      CompoundWord includes ExtGlobPart
    """
        left_token = self.cur_token
        arms = []  # type: List[word_t]
        spids = []
        spids.append(left_token.span_id)

        self.lexer.PushHint(Id.Op_RParen, Id.Right_ExtGlob)
        self._Next(lex_mode_e.ExtGlob)  # advance past LEFT

        read_word = False  # did we just a read a word?  To handle @(||).

        while True:
            self._Peek()

            if self.token_type == Id.Right_ExtGlob:
                if not read_word:
                    arms.append(osh_word.CompoundWord())
                spids.append(self.cur_token.span_id)
                break

            elif self.token_type == Id.Op_Pipe:
                if not read_word:
                    arms.append(osh_word.CompoundWord())
                read_word = False
                self._Next(lex_mode_e.ExtGlob)

            # lex mode EXTGLOB should only produce these 4 kinds of tokens
            elif self.token_kind in (Kind.Lit, Kind.Left, Kind.VSub,
                                     Kind.ExtGlob):
                w = self._ReadCompoundWord(lex_mode=lex_mode_e.ExtGlob)
                arms.append(w)
                read_word = True

            elif self.token_kind == Kind.Eof:
                p_die('Unexpected EOF reading extended glob that began here',
                      token=left_token)

            else:
                raise AssertionError('Unexpected token %r' % self.cur_token)

        part = word_part.ExtGlobPart(left_token, arms)
        part.spids.extend(spids)
        return part
Example #6
0
def _TwoArgs(argv):
    """Returns an expression tree to be evaluated."""
    a0, a1 = argv
    if a0 == '!':
        return ast.LogicalNot(_StringWordTest(a1))
    unary_id = _UNARY_LOOKUP.get(a0)
    if unary_id is None:
        # TODO:
        # - syntax error
        # - separate lookup by unary
        util.p_die('Expected unary operator, got %r (2 args)', a0)
    child = ast.StringWord(Id.Word_Compound, a1)
    return ast.BoolUnary(unary_id, child)
Example #7
0
def _TwoArgs(w_parser):
  # type: (_StringWordEmitter) -> bool_expr_t
  """Returns an expression tree to be evaluated."""
  w0 = w_parser.Read()
  w1 = w_parser.Read()
  if w0.s == '!':
    return bool_expr.LogicalNot(bool_expr.WordTest(w1))
  unary_id = match.BracketUnary(w0.s)
  if unary_id == -1:
    # TODO:
    # - separate lookup by unary
    p_die('Expected unary operator, got %r (2 args)', w0.s, word=w0)
  return bool_expr.Unary(unary_id, w1)
Example #8
0
  def ParseProc(self, node):
    # type: (command__Proc) -> None

    # proc name-with-hyphens() must be accepted
    self._Next(lex_mode_e.ShCommand) 
    self._Peek()
    # example: 'proc f[' gets you Lit_ArrayLhsOpen
    if self.token_type != Id.Lit_Chars:
      p_die('Invalid proc name %r', self.cur_token.val, token=self.cur_token)
    node.name = self.cur_token

    last_token = self.parse_ctx.ParseProc(self.lexer, node)
    if last_token.id == Id.Op_LBrace:  # Translate to what CommandParser wants
      last_token.id = Id.Lit_LBrace
    self.buffered_word = word.Token(last_token)
    self._Next(lex_mode_e.ShCommand)  # required
Example #9
0
  def _ReadLikeDQ(self, left_dq_token, out_parts):
    # type: (Optional[Token], List[word_part_t]) -> None
    """
    Args:
      left_dq_token: A token if we are reading a double quoted part, or None if
        we're reading a here doc.
      out_parts: list of word_part to append to
    """
    done = False
    while not done:
      self._Next(lex_mode_e.DQ)
      self._Peek()

      if self.token_kind == Kind.Lit:
        if self.token_type == Id.Lit_EscapedChar:
          part = word_part.EscapedLiteral(self.cur_token)  # type: word_part_t
        else:
          part = self.cur_token
        out_parts.append(part)

      elif self.token_kind == Kind.Left:
        part = self._ReadDoubleQuotedLeftParts()
        out_parts.append(part)

      elif self.token_kind == Kind.VSub:
        part = simple_var_sub(self.cur_token)
        out_parts.append(part)
        # NOTE: parsing "$f(x)" would BREAK CODE.  Could add a more for it
        # later.

      elif self.token_kind == Kind.Right:
        assert self.token_type == Id.Right_DoubleQuote, self.token_type
        if left_dq_token:
          done = True
        else:
          # In a here doc, the right quote is literal!
          out_parts.append(self.cur_token)

      elif self.token_kind == Kind.Eof:
        if left_dq_token:
          p_die('Unexpected EOF reading double-quoted string that began here',
                token=left_dq_token)
        else:  # here docs will have an EOF in their token stream
          done = True

      else:
        raise AssertionError(self.cur_token)
Example #10
0
  def _NameInClass(self, negated_tok, tok):
    # type: (token, token) -> class_literal_term_t
    """
    Like the above, but 'dot' doesn't mean anything.
    """
    if negated_tok:  # For error messages
      negated_speck = speck(negated_tok.id, negated_tok.span_id)
    else:
      negated_speck = None

    val = tok.val
    if val in self.POSIX_CLASSES:
      return posix_class(negated_speck, val)

    perl = self.PERL_CLASSES.get(val)
    if perl:
      return perl_class(negated_speck, perl)
    p_die("%r isn't a character class", val, token=tok)
Example #11
0
    def _ReadArithSub2Part(self):
        # type: () -> word_part__ArithSubPart
        """Non-standard arith sub $[a + 1]."""
        left_span_id = self.cur_token.span_id

        self._Next(lex_mode_e.Arith)
        anode = self._ReadArithExpr()
        if self.token_type != Id.Arith_RBracket:
            p_die('Expected ], got %r',
                  self.cur_token.val,
                  token=self.cur_token)

        right_span_id = self.cur_token.span_id

        node = word_part.ArithSubPart(anode)
        node.spids.append(left_span_id)
        node.spids.append(right_span_id)
        return node
Example #12
0
    def _ReadArithSubPart(self):
        # type: () -> word_part__ArithSubPart
        """
    Read an arith substitution, which contains an arith expression, e.g.
    $((a + 1)).
    """
        left_span_id = self.cur_token.span_id

        # The second one needs to be disambiguated in stuff like stuff like:
        # $(echo $(( 1+2 )) )
        self.lexer.PushHint(Id.Op_RParen, Id.Right_ArithSub)

        # NOTE: To disambiguate $(( as arith sub vs. command sub and subshell, we
        # could save the lexer/reader state here, and retry if the arithmetic parse
        # fails.  But we can almost always catch this at parse time.  There could
        # be some exceptions like:
        # $((echo * foo))  # looks like multiplication
        # $((echo / foo))  # looks like division

        self._Next(lex_mode_e.Arith)
        anode = self._ReadArithExpr()
        if self.token_type != Id.Arith_RParen:
            p_die('Expected first ) to end arith sub, got %r',
                  self.cur_token.val,
                  token=self.cur_token)

        self._Next(lex_mode_e.Outer)  # TODO: This could be DQ or ARITH too

        # PROBLEM: $(echo $(( 1 + 2 )) )
        # Two right parens break the Id.Eof_RParen scheme
        self._Peek()
        if self.token_type != Id.Right_ArithSub:
            p_die('Expected second ) to end arith sub, got %r',
                  self.cur_token.val,
                  token=self.cur_token)

        right_span_id = self.cur_token.span_id

        node = word_part.ArithSubPart(anode)
        node.spids.append(left_span_id)
        node.spids.append(right_span_id)
        return node
Example #13
0
  def ParseVarDecl(self, kw_token, lexer):
    # type: (Token, Lexer) -> Tuple[command__VarDecl, Token]
    """ var mylist = [1, 2, 3] """

    # TODO: We do need re-entrancy for var x = @[ (1+2) ] and such
    if self.parsing_expr:
      p_die("ShAssignment expression can't be nested like this", token=kw_token)

    self.parsing_expr = True
    try:
      pnode, last_token = self.e_parser.Parse(lexer, grammar_nt.oil_var_decl)
    finally:
      self.parsing_expr = False

    if 0:
      self.p_printer.Print(pnode)

    ast_node = self.tr.MakeVarDecl(pnode)
    ast_node.keyword = kw_token  # VarDecl didn't fill this in
    return ast_node, last_token
Example #14
0
def _Classify(gr, tok):
    # type: (Grammar, token) -> int

    # We have to match up what ParserGenerator.make_grammar() did when
    # calling make_label() and make_first().  See classify() in
    # opy/pgen2/driver.py.

    # 'x' and 'for' are both tokenized as Expr_Name.  This handles the 'for'
    # case.
    if tok.id == Id.Expr_Name:
        if tok.val in gr.keywords:
            return gr.keywords[tok.val]

    # This handles 'x'.
    typ = tok.id.enum_id
    if typ in gr.tokens:
        return gr.tokens[typ]

    type_str = '' if tok.id == Id.Unknown_Tok else (' (%s)' % tok.id.name)
    p_die('Unexpected token in expression mode%s', type_str, token=tok)
Example #15
0
    def _Tuple(self, children):
        # type: (List[PNode]) -> expr_t

        n = len(children)

        # (x) -- not a tuple
        if n == 1:
            return self.Expr(children[0])

        # x, and (x,) aren't allowed
        if n == 2:
            p_die('Write singleton tuples with tup(), not a trailing comma',
                  token=children[1].tok)

        elts = []  # type: List[expr_t]
        for i in xrange(0, n, 2):  # skip commas
            p_node = children[i]
            elts.append(self.Expr(p_node))

        return expr.Tuple(elts, expr_context_e.Store)  # unused expr_context_e
Example #16
0
    def _ReadArithWord(self):
        # type: () -> Tuple[word_t, bool]
        """Helper function for ReadArithWord."""
        self._Peek()

        if self.token_kind == Kind.Unknown:
            p_die('Unexpected token in arithmetic context',
                  token=self.cur_token)

        elif self.token_kind == Kind.Eof:
            # Just return EOF token
            w = osh_word.TokenWord(self.cur_token)  # type: word_t
            return w, False

        elif self.token_kind == Kind.Ignored:
            # Space should be ignored.  TODO: change this to SPACE_SPACE and
            # SPACE_NEWLINE?  or SPACE_TOK.
            self._Next(lex_mode_e.Arith)
            return None, True  # Tell wrapper to try again

        elif self.token_kind in (Kind.Arith, Kind.Right):
            # Id.Right_ArithSub IS just a normal token, handled by ArithParser
            self._Next(lex_mode_e.Arith)
            w = osh_word.TokenWord(self.cur_token)
            return w, False

        elif self.token_kind in (Kind.Lit, Kind.Left):
            w = self._ReadCompoundWord(lex_mode=lex_mode_e.Arith)
            return w, False

        elif self.token_kind == Kind.VSub:
            part = word_part.SimpleVarSub(self.cur_token)
            self._Next(lex_mode_e.Arith)
            w = osh_word.CompoundWord([part])
            return w, False

        else:
            assert False, ("Unexpected token parsing arith sub: %s" %
                           self.cur_token)

        raise AssertionError("Shouldn't get here")
Example #17
0
  def _ReadSliceVarOp(self):
    # type: () -> suffix_op__Slice
    """ VarOf ':' ArithExpr (':' ArithExpr )? """
    self._Next(lex_mode_e.Arith)
    self._Peek()
    if self.token_type == Id.Arith_Colon:  # A pun for Id.VOp2_Colon
      begin = None  # no beginning specified
    else:
      begin = self._ReadArithExpr()

    if self.token_type == Id.Arith_RBrace:
      return suffix_op.Slice(begin, None)  # No length specified

    # Id.Arith_Colon is a pun for Id.VOp2_Colon
    if self.token_type == Id.Arith_Colon:
      self._Next(lex_mode_e.Arith)
      length = self._ReadArithExpr()
      return suffix_op.Slice(begin, length)

    p_die("Unexpected token in slice: %r", self.cur_token.val,
          token=self.cur_token)
Example #18
0
def _Classify(gr, tok):
  # type: (Grammar, token) -> int

  # We have to match up what ParserGenerator.make_grammar() did when
  # calling make_label() and make_first().  See classify() in
  # opy/pgen2/driver.py.

  # 'x' and 'for' are both tokenized as Expr_Name.  This handles the 'for'
  # case.
  if tok.id == Id.Expr_Name:
    ilabel = gr.keywords.get(tok.val)
    if ilabel is not None:
      return ilabel

  # This handles 'x'.
  typ = tok.id.enum_id
  ilabel = gr.tokens.get(typ)
  if ilabel is not None:
    return ilabel

  p_die('Invalid token %s', tok, token=tok)
Example #19
0
 def _PlaceList(self, p_node):
     # type: (PNode) -> List[place_expr_t]
     """
 place_list: expr (',' expr)*
 """
     assert p_node.typ == grammar_nt.place_list
     places = []  # type: List[place_expr_t]
     for p in p_node.children[::2]:
         e = self.Expr(p)
         if e.tag == expr_e.Var:  # COMPATIBILITY hack
             assert isinstance(e, expr__Var)
             places.append(place_expr.Var(e.name))
         elif e.tag in (place_expr_e.Var, place_expr_e.Subscript,
                        place_expr_e.Attribute):
             places.append(cast(place_expr_t, e))
         else:
             # This blame mechanism seems to work.  Otherwise we don't have a method
             # to blame an arbitrary expr_t.
             p_die("Can't assign to this expression",
                   token=p.tok if p.tok else None)
     return places
Example #20
0
  def _NameInRegex(self, negated_tok, tok):
    # type: (token, token) -> re_t

    if negated_tok:  # For error messages
      negated_speck = speck(negated_tok.id, negated_tok.span_id)
    else:
      negated_speck = None

    val = tok.val
    if val == 'dot':
      if negated_tok:
        p_die("Can't negate this symbol", token=tok)
      return tok

    if val in self.POSIX_CLASSES:
      return posix_class(negated_speck, val)

    perl = self.PERL_CLASSES.get(val)
    if perl:
      return perl_class(negated_speck, perl)

    p_die("%r isn't a character class", val, token=tok)
Example #21
0
  def _ReadSliceVarOp(self):
    # type: () -> suffix_op__Slice
    """ VarOf ':' ArithExpr (':' ArithExpr )? """
    self._Next(lex_mode_e.Arith)
    self._Peek()
    if self.token_type == Id.Arith_Colon:  # A pun for Id.VOp2_Colon
      # no beginning specified
      begin = None  # type: Optional[arith_expr_t]
    else:
      begin = self._ReadArithExpr()

    if self.token_type == Id.Arith_RBrace:
      no_length = None  # type: Optional[arith_expr_t]  # No length specified
      return suffix_op.Slice(begin, no_length)

    # Id.Arith_Colon is a pun for Id.VOp2_Colon
    if self.token_type == Id.Arith_Colon:
      self._Next(lex_mode_e.Arith)
      length = self._ReadArithExpr()
      return suffix_op.Slice(begin, length)

    p_die("Expected : or } in slice", token=self.cur_token)
Example #22
0
    def _ReadPatSubVarOp(self, lex_mode):
        # type: (lex_mode_t) -> suffix_op__PatSub
        """
    Match     = ('/' | '#' | '%') WORD
    VarSub    = ...
              | VarOf '/' Match '/' WORD
    """
        pat = self._ReadVarOpArg(lex_mode,
                                 eof_type=Id.Lit_Slash,
                                 empty_ok=False)
        assert isinstance(pat, word__CompoundWord)  # Because empty_ok=False

        if len(pat.parts) == 1:
            ok, s, quoted = word.StaticEval(pat)
            if ok and s == '/' and not quoted:  # Looks like ${a////c}, read again
                self._Next(lex_mode)
                self._Peek()
                p = word_part.LiteralPart(self.cur_token)
                pat.parts.append(p)

        if len(pat.parts) == 0:
            p_die('Pattern in ${x/pat/replace} must not be empty',
                  token=self.cur_token)

        replace_mode = Id.Undefined_Tok
        # Check for / # % modifier on pattern.
        first_part = pat.parts[0]
        if isinstance(first_part, word_part__LiteralPart):
            lit_id = first_part.token.id
            if lit_id in (Id.Lit_Slash, Id.Lit_Pound, Id.Lit_Percent):
                pat.parts.pop(0)
                replace_mode = lit_id

        # NOTE: If there is a modifier, the pattern can be empty, e.g.
        # ${s/#/foo} and ${a/%/foo}.

        if self.token_type == Id.Right_VarSub:
            # e.g. ${v/a} is the same as ${v/a/}  -- empty replacement string
            return suffix_op.PatSub(pat, None, replace_mode)

        if self.token_type == Id.Lit_Slash:
            replace = self._ReadVarOpArg(lex_mode)  # do not stop at /

            self._Peek()
            if self.token_type != Id.Right_VarSub:
                # NOTE: I think this never happens.
                # We're either in the VS_ARG_UNQ or VS_ARG_DQ lex state, and everything
                # there is Lit_ or Left_, except for }.
                p_die("Expected } after replacement string, got %s",
                      self.cur_token,
                      token=self.cur_token)

            return suffix_op.PatSub(pat, replace, replace_mode)

        # Happens with ${x//} and ${x///foo}, see test/parse-errors.sh
        p_die("Expected } after pat sub, got %r",
              self.cur_token.val,
              token=self.cur_token)
Example #23
0
  def ReadForExpression(self):
    # type: () -> command__ForExpr
    """Read ((i=0; i<5; ++i)) -- part of command context."""
    self._NextNonSpace()  # skip over ((

    self._Peek()
    if self.token_type == Id.Arith_Semi:  # for (( ; i < 10; i++ ))
      init_node = None  # type: Optional[arith_expr_t]
    else:
      init_node = self._ReadArithExpr()
    self._NextNonSpace()

    self._Peek()
    if self.token_type == Id.Arith_Semi:  # for (( ; ; i++ ))
      cond_node = None  # type: Optional[arith_expr_t]
    else:
      cond_node = self._ReadArithExpr()
    self._NextNonSpace()

    self._Peek()
    if self.token_type == Id.Arith_RParen:  # for (( ; ; ))
      update_node = None  # type: Optional[arith_expr_t]
    else:
      update_node = self._ReadArithExpr()
    self._NextNonSpace()

    self._Peek()
    if self.token_type != Id.Arith_RParen:
      p_die('Expected ) to end for loop expression', token=self.cur_token)
    self._Next(lex_mode_e.ShCommand)

    node = command.ForExpr()
    node.init = init_node
    node.cond = cond_node
    node.update = update_node
    return node
 def _PlaceList(self, p_node):
   # type: (PNode) -> List[place_expr_t]
   """
   place_list: expr (',' expr)*
   """
   assert p_node.typ == grammar_nt.place_list
   places = []  # type: List[place_expr_t]
   n = len(p_node.children)
   for i in xrange(0, n, 2):  # was children[::2]
     p = p_node.children[i]
     e = self.Expr(p)
     UP_e = e
     tag = e.tag_()
     if tag == expr_e.Var:  # COMPATIBILITY hack
       e = cast(expr__Var, UP_e)
       places.append(place_expr.Var(e.name))
     elif tag in (
         place_expr_e.Var, place_expr_e.Subscript, place_expr_e.Attribute):
       places.append(cast(place_expr_t, UP_e))
     else:
       # This blame mechanism seems to work.  Otherwise we don't have a method
       # to blame an arbitrary expr_t.
       p_die("Can't assign to this expression", token=p.tok if p.tok else None)
   return places
Example #25
0
    def Parse(self, lexer, start_symbol):
        # type: (Lexer, int) -> Tuple[PNode, Token]

        # Reuse the parser
        self.push_parser.setup(start_symbol)
        try:
            last_token = _PushOilTokens(self.parse_ctx, self.gr,
                                        self.push_parser, lexer)
        except parse.ParseError as e:
            #log('ERROR %s', e)
            # TODO:
            # - Describe what lexer mode we're in (Invalid syntax in regex)
            #   - Maybe say where the mode started
            # - Id.Unknown_Tok could say "This character is invalid"

            # ParseError has a "too much input" case but I haven't been able to
            # tickle it.  Mabye it's because of the Eof tokens?

            p_die('Syntax error in expression (near %s)',
                  ui.PrettyId(e.tok.id),
                  token=e.tok)
            #raise error.Parse('Syntax error in expression', token=e.tok)

        return self.push_parser.rootnode, last_token
Example #26
0
    def Parse(self):
        self._Next(lex_mode_e.PrintfOuter)
        parts = []
        while True:
            if (self.token_kind == Kind.Char
                    or self.token_type == Id.Format_EscapedPercent):

                # TODO: Could handle Char_BadBackslash.
                # Maybe make that a different kind?

                parts.append(printf_part.Literal(self.cur_token))

            elif self.token_type == Id.Format_Percent:
                parts.append(self._ParseFormatStr())

            elif self.token_type == Id.Eof_Real:
                break

            else:
                p_die('Invalid token %r', token=self.cur_token)

            self._Next(lex_mode_e.PrintfOuter)

        return parts
Example #27
0
  def _ReadPatSubVarOp(self):
    # type: () -> suffix_op__PatSub
    """
    Match     = ('/' | '#' | '%') WORD
    VarSub    = ...
              | VarOf '/' Match '/' WORD
    """
    # Exception: VSub_ArgUnquoted even if it's quoted
    # stop at eof_type=Lit_Slash, empty_ok=False
    UP_pat = self._ReadVarOpArg3(lex_mode_e.VSub_ArgUnquoted, Id.Lit_Slash, False)
    assert UP_pat.tag_() == word_e.Compound, UP_pat  # Because empty_ok=False
    pat = cast(compound_word, UP_pat)

    if len(pat.parts) == 1:
      ok, s, quoted = word_.StaticEval(pat)
      if ok and s == '/' and not quoted:  # Looks like ${a////c}, read again
        self._Next(lex_mode_e.VSub_ArgUnquoted)
        self._Peek()
        pat.parts.append(self.cur_token)

    if len(pat.parts) == 0:
      p_die('Pattern in ${x/pat/replace} must not be empty',
            token=self.cur_token)

    replace_mode = Id.Undefined_Tok
    # Check for / # % modifier on pattern.
    UP_first_part = pat.parts[0]
    if UP_first_part.tag_() == word_part_e.Literal:
      lit_id = cast(Token, UP_first_part).id
      if lit_id in (Id.Lit_Slash, Id.Lit_Pound, Id.Lit_Percent):
        pat.parts.pop(0)
        replace_mode = lit_id

    # NOTE: If there is a modifier, the pattern can be empty, e.g.
    # ${s/#/foo} and ${a/%/foo}.

    if self.token_type == Id.Right_DollarBrace:
      # e.g. ${v/a} is the same as ${v/a/}  -- empty replacement string
      return suffix_op.PatSub(pat, None, replace_mode)

    if self.token_type == Id.Lit_Slash:
      replace = self._ReadVarOpArg(lex_mode_e.VSub_ArgUnquoted)  # do not stop at /

      self._Peek()
      if self.token_type != Id.Right_DollarBrace:
        # NOTE: I think this never happens.
        # We're either in the VS_ARG_UNQ or VS_ARG_DQ lex state, and everything
        # there is Lit_ or Left_, except for }.
        p_die("Expected } after replacement string, got %s",
              ui.PrettyId(self.token_type), token=self.cur_token)

      return suffix_op.PatSub(pat, replace, replace_mode)

    # Happens with ${x//} and ${x///foo}, see test/parse-errors.sh
    p_die('Expected } or / to close pattern', token=self.cur_token)
Example #28
0
    def _RangeChar(self, p_node):
        # type: (PNode) -> str
        """Evaluate a range endpoints.
    - the 'a' in 'a'-'z'
    - the \x00 in \x00-\x01
    etc.

    TODO: This function doesn't respect the LST invariant.
    """
        assert p_node.typ == grammar_nt.range_char, p_node
        children = p_node.children
        typ = children[0].typ
        if ISNONTERMINAL(typ):
            # 'a' in 'a'-'b'
            if typ == grammar_nt.sq_string:
                sq_part = cast(single_quoted, children[0].children[1].tok)
                tokens = sq_part.tokens
                if len(
                        tokens
                ) > 1:  # Can happen with multiline single-quoted strings
                    p_die(RANGE_POINT_TOO_LONG, part=sq_part)
                if len(tokens[0].val) > 1:
                    p_die(RANGE_POINT_TOO_LONG, part=sq_part)
                s = tokens[0].val[0]
                return s

            if typ == grammar_nt.char_literal:
                raise AssertionError('TODO')
                # TODO: This brings in a lot of dependencies, and this type checking
                # errors.  We want to respect the LST invariant anyway.

                #from osh import word_compile
                #tok = children[0].children[0].tok
                #s = word_compile.EvalCStringToken(tok.id, tok.val)
                #return s

            raise NotImplementedError()
        else:
            # Expr_Name or Expr_DecInt
            tok = p_node.tok
            if tok.id in (Id.Expr_Name, Id.Expr_DecInt):
                # For the a in a-z, 0 in 0-9
                if len(tok.val) != 1:
                    p_die(RANGE_POINT_TOO_LONG, token=tok)
                return tok.val[0]

            raise NotImplementedError()
Example #29
0
def NullError(p, t, bp):
    # type: (TdopParser, word_t, int) -> arith_expr_t
    # TODO: I need position information
    p_die("Token can't be used in prefix position", word=t)
    return None  # never reached
Example #30
0
def LeftError(p, t, left, rbp):
    # type: (TdopParser, word_t, arith_expr_t, int) -> arith_expr_t
    # Hm is this not called because of binding power?
    p_die("Token can't be used in infix position", word=t)
    return None  # never reached