code: def expr_str(expr, sc_expr_str_fn=standard_sc_expr_str):
      
      if expr.__class__ is not tuple:
          return sc_expr_str_fn(expr)
  
      if expr[0] is AND:
          return "{} && {}".format(_parenthesize(expr[1], OR, sc_expr_str_fn),
                                   _parenthesize(expr[2], OR, sc_expr_str_fn))
  
      if expr[0] is OR:
          # This turns A && B || C && D into "(A && B) || (C && D)", which is
          # redundant, but more readable
          return "{} || {}".format(_parenthesize(expr[1], AND, sc_expr_str_fn),
                                   _parenthesize(expr[2], AND, sc_expr_str_fn))
  
      if expr[0] is NOT:
          if expr[1].__class__ is tuple:
              return "!({})".format(expr_str(expr[1], sc_expr_str_fn))
          return "!" + sc_expr_str_fn(expr[1])  # Symbol
  
      # Relation
      #
      # Relation operands are always symbols (quoted strings are constant
      # symbols)
      return "{} {} {}".format(sc_expr_str_fn(expr[1]), _REL_TO_STR[expr[0]],
                               sc_expr_str_fn(expr[2]))
docs: Returns the string representation of the expression 'expr', as in a Kconfig
      file.
  
      Passing subexpressions of expressions to this function works as expected.
  
      sc_expr_str_fn (default: standard_sc_expr_str):
        This function is called for every symbol/choice (hence "sc") appearing in
        the expression, with the symbol/choice as the argument. It is expected to
        return a string to be used for the symbol/choice.
  
        This can be used e.g. to turn symbols/choices into links when generating
        documentation, or for printing the value of each symbol/choice after it.
  
        Note that quoted values are represented as constants symbols
        (Symbol.is_constant == True).