def _ThreeArgs(argv): """Returns an expression tree to be evaluated.""" a0, a1, a2 = argv # NOTE: Order is important here. binary_id = _BINARY_LOOKUP.get(a1) if binary_id is not None: left = ast.StringWord(Id.Word_Compound, a0) right = ast.StringWord(Id.Word_Compound, a2) return ast.BoolBinary(binary_id, left, right) if a1 == '-a': left = _StringWordTest(a0) right = _StringWordTest(a2) return ast.LogicalAnd(left, right) if a1 == '-o': left = _StringWordTest(a0) right = _StringWordTest(a2) return ast.LogicalOr(left, right) if a0 == '!': child = _TwoArgs(argv[1:]) return ast.LogicalNot(child) if a0 == '(' and a2 == ')': return _StringWordTest(a1) util.p_die('Syntax error: binary operator expected, got %r (3 args)', a1)
def ParseFactor(self): """ Factor : WORD | UNARY_OP WORD | WORD BINARY_OP WORD | '(' Expr ')' """ if self.b_kind == Kind.BoolUnary: # Just save the type and not the token itself? op = self.op_id if not self._Next(): return None w = self.cur_word if not self._Next(): return None node = ast.BoolUnary(op, w) return node if self.b_kind == Kind.Word: # Peek ahead another token. t2 = self._LookAhead() t2_op_id = word.BoolId(t2) t2_b_kind = LookupKind(t2_op_id) #log('t2 %s / t2_op_id %s / t2_b_kind %s', t2, t2_op_id, t2_b_kind) # Redir pun for < and >, -a and -o pun if t2_b_kind in (Kind.BoolBinary, Kind.Redir): left = self.cur_word if not self._Next(): return None op = self.op_id # TODO: Need to change to lex_mode_e.BASH_REGEX. # _Next(lex_mode) then? is_regex = t2_op_id == Id.BoolBinary_EqualTilde if is_regex: if not self._Next(lex_mode=lex_mode_e.BASH_REGEX): return None else: if not self._Next(): return None right = self.cur_word if is_regex: # TODO: Quoted parts need to be regex-escaped, e.g. [[ $a =~ "{" ]]. # I don't think libc has a function to do this. Escape these # characters: # https://www.gnu.org/software/sed/manual/html_node/ERE-syntax.html0 ok, regex_str, unused_quoted = word.StaticEval(right) # doesn't contain $foo, etc. if ok and not libc.regex_parse(regex_str): self.AddErrorContext("Invalid regex: %r" % regex_str, word=right) return None if not self._Next(): return None return ast.BoolBinary(op, left, right) else: # [[ foo ]] w = self.cur_word if not self._Next(): return None return ast.WordTest(w) if self.op_id == Id.Op_LParen: if not self._Next(): return None node = self.ParseExpr() if self.op_id != Id.Op_RParen: self.AddErrorContext('Expected ), got %s', self.cur_word, word=self.cur_word) return None if not self._Next(): return None return node # TODO: A proper error, e.g. for [[ && ]] or [[ ]] self.AddErrorContext('Unexpected token: %s' % self.cur_word, word=self.cur_word) return None
def ParseFactor(self): """ Factor : WORD | UNARY_OP WORD | WORD BINARY_OP WORD | '(' Expr ')' """ if self.b_kind == Kind.BoolUnary: # Just save the type and not the token itself? op = self.op_id self._Next() w = self.cur_word # e.g. [[ -f < ]]. But [[ -f '<' ]] is OK if w.tag not in (word_e.CompoundWord, word_e.StringWord): p_die('Invalid argument to unary operator', word=w) self._Next() node = ast.BoolUnary(op, w) return node if self.b_kind == Kind.Word: # Peek ahead another token. t2 = self._LookAhead() t2_op_id = word.BoolId(t2) t2_b_kind = LookupKind(t2_op_id) #log('t2 %s / t2_op_id %s / t2_b_kind %s', t2, t2_op_id, t2_b_kind) # Redir pun for < and >, -a and -o pun if t2_b_kind in (Kind.BoolBinary, Kind.Redir): left = self.cur_word self._Next() op = self.op_id # TODO: Need to change to lex_mode_e.BASH_REGEX. # _Next(lex_mode) then? is_regex = t2_op_id == Id.BoolBinary_EqualTilde if is_regex: self._Next(lex_mode=lex_mode_e.BASH_REGEX) else: self._Next() right = self.cur_word if is_regex: # NOTE: StaticEval for checking regex syntax isn't enough. We could # need to pass do_ere so that the quoted parts get escaped. #ok, s, unused_quoted = word.StaticEval(right) pass self._Next() return ast.BoolBinary(op, left, right) else: # [[ foo ]] w = self.cur_word self._Next() return ast.WordTest(w) if self.op_id == Id.Op_LParen: self._Next() node = self.ParseExpr() if self.op_id != Id.Op_RParen: p_die('Expected ), got %s', self.cur_word, word=self.cur_word) self._Next() return node # It's not WORD, UNARY_OP, or '(' p_die('Unexpected token in boolean expression', word=self.cur_word)