view libinterp/parse-tree/pt-binop.cc @ 17138:96526baf7423

Fix reading of images using PixelRegion with ranges ending before the limit. * __magick_read__.cc (calculate_region): reduce image cache being loaded into memory by checking max() instead of limit(). Simplify calculation of output size by using nelem(). Fix calculation of the pointer shift required when moving to next column in cases where limit() and max () were different.
author Carnë Draug <carandraug@octave.org>
date Thu, 01 Aug 2013 18:37:07 +0100
parents 049e8bbff782
children bc924baa2c4e
line wrap: on
line source

/*

Copyright (C) 1996-2012 John W. Eaton

This file is part of Octave.

Octave is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version.

Octave is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with Octave; see the file COPYING.  If not, see
<http://www.gnu.org/licenses/>.

*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "error.h"
#include "defun.h"
#include "oct-obj.h"
#include "ov.h"
#include "profiler.h"
#include "pt-binop.h"
#include "pt-bp.h"
#include "pt-walk.h"
#include "variables.h"

// TRUE means we mark | and & expressions for braindead short-circuit
// behavior.
static bool Vdo_braindead_shortcircuit_evaluation;

// Binary expressions.

octave_value_list
tree_binary_expression::rvalue (int nargout)
{
  octave_value_list retval;

  if (nargout > 1)
    error ("binary operator '%s': invalid number of output arguments",
           oper () . c_str ());
  else
    retval = rvalue1 (nargout);

  return retval;
}

octave_value
tree_binary_expression::rvalue1 (int)
{
  octave_value retval;

  if (error_state)
    return retval;

  if (Vdo_braindead_shortcircuit_evaluation
      && eligible_for_braindead_shortcircuit)
    {
      if (op_lhs)
        {
          octave_value a = op_lhs->rvalue1 ();

          if (! error_state)
            {
              if (a.ndims () == 2 && a.rows () == 1 && a.columns () == 1)
                {
                  bool result = false;

                  bool a_true = a.is_true ();

                  if (! error_state)
                    {
                      if (a_true)
                        {
                          if (etype == octave_value::op_el_or)
                            {
                              result = true;
                              goto done;
                            }
                        }
                      else
                        {
                          if (etype == octave_value::op_el_and)
                            goto done;
                        }

                      if (op_rhs)
                        {
                          octave_value b = op_rhs->rvalue1 ();

                          if (! error_state)
                            result = b.is_true ();
                        }

                    done:

                      if (! error_state)
                        return octave_value (result);
                    }
                }
            }
        }
    }

  if (op_lhs)
    {
      octave_value a = op_lhs->rvalue1 ();

      if (! error_state && a.is_defined () && op_rhs)
        {
          octave_value b = op_rhs->rvalue1 ();

          if (! error_state && b.is_defined ())
            {
              BEGIN_PROFILER_BLOCK ("binary " + oper ())

              // Note: The profiler does not catch the braindead
              // short-circuit evaluation code above, but that should be
              // ok. The evaluation of operands and the operator itself
              // is entangled and it's not clear where to start/stop
              // timing the operator to make it reasonable.

              retval = ::do_binary_op (etype, a, b);

              if (error_state)
                retval = octave_value ();

              END_PROFILER_BLOCK
            }
        }
    }

  return retval;
}

std::string
tree_binary_expression::oper (void) const
{
  return octave_value::binary_op_as_string (etype);
}

tree_expression *
tree_binary_expression::dup (symbol_table::scope_id scope,
                             symbol_table::context_id context) const
{
  tree_binary_expression *new_be
    = new tree_binary_expression (op_lhs ? op_lhs->dup (scope, context) : 0,
                                  op_rhs ? op_rhs->dup (scope, context) : 0,
                                  line (), column (), etype);

  new_be->copy_base (*this);

  return new_be;
}

void
tree_binary_expression::accept (tree_walker& tw)
{
  tw.visit_binary_expression (*this);
}

// Boolean expressions.

octave_value_list
tree_boolean_expression::rvalue (int nargout)
{
  octave_value_list retval;

  if (nargout > 1)
    error ("binary operator '%s': invalid number of output arguments",
           oper () . c_str ());
  else
    retval = rvalue1 (nargout);

  return retval;
}

octave_value
tree_boolean_expression::rvalue1 (int)
{
  octave_value retval;

  if (error_state)
    return retval;

  bool result = false;

  // This evaluation is not caught by the profiler, since we can't find
  // a reasonable place where to time. Note that we don't want to
  // include evaluation of LHS or RHS into the timing, but this is
  // entangled together with short-circuit evaluation here.

  if (op_lhs)
    {
      octave_value a = op_lhs->rvalue1 ();

      if (! error_state)
        {
          bool a_true = a.is_true ();

          if (! error_state)
            {
              if (a_true)
                {
                  if (etype == bool_or)
                    {
                      result = true;
                      goto done;
                    }
                }
              else
                {
                  if (etype == bool_and)
                    goto done;
                }

              if (op_rhs)
                {
                  octave_value b = op_rhs->rvalue1 ();

                  if (! error_state)
                    result = b.is_true ();
                }

            done:

              if (! error_state)
                retval = octave_value (result);
            }
        }
    }

  return retval;
}

std::string
tree_boolean_expression::oper (void) const
{
  std::string retval = "<unknown>";

  switch (etype)
    {
    case bool_and:
      retval = "&&";
      break;

    case bool_or:
      retval = "||";
      break;

    default:
      break;
    }

  return retval;
}

tree_expression *
tree_boolean_expression::dup (symbol_table::scope_id scope,
                              symbol_table::context_id context) const
{
  tree_boolean_expression *new_be
    = new tree_boolean_expression (op_lhs ? op_lhs->dup (scope, context) : 0,
                                   op_rhs ? op_rhs->dup (scope, context) : 0,
                                   line (), column (), etype);

  new_be->copy_base (*this);

  return new_be;
}

DEFUN (do_braindead_shortcircuit_evaluation, args, nargout,
  "-*- texinfo -*-\n\
@deftypefn  {Built-in Function} {@var{val} =} do_braindead_shortcircuit_evaluation ()\n\
@deftypefnx {Built-in Function} {@var{old_val} =} do_braindead_shortcircuit_evaluation (@var{new_val})\n\
@deftypefnx {Built-in Function} {} do_braindead_shortcircuit_evaluation (@var{new_val}, \"local\")\n\
Query or set the internal variable that controls whether Octave will\n\
do short-circuit evaluation of @samp{|} and @samp{&} operators inside the\n\
conditions of if or while statements.\n\
\n\
This feature is only provided for compatibility with @sc{matlab} and should\n\
not be used unless you are porting old code that relies on this feature.\n\
\n\
To obtain short-circuit behavior for logical expressions in new programs,\n\
you should always use the @samp{&&} and @samp{||} operators.\n\
\n\
When called from inside a function with the \"local\" option, the variable is\n\
changed locally for the function and any subroutines it calls.  The original\n\
variable value is restored when exiting the function.\n\
@end deftypefn")
{
  return SET_INTERNAL_VARIABLE (do_braindead_shortcircuit_evaluation);
}

/*
%!test
%! x = 0;
%! do_braindead_shortcircuit_evaluation (0);
%! if (1 | (x = 1))
%! endif
%! assert (x, 1);
%! do_braindead_shortcircuit_evaluation (1);
%! if (1 | (x = 0))
%! endif
%! assert (x, 1);
*/