/*
** cmp.ops
*/

VERSION = PARROT_VERSION;

=head1 NAME

cmp.ops - Register Comparison Ops

=cut

=head1 DESCRIPTION

Operations that compare two registers against each other.
Some of these operations affect control flow directly; others
do not.

The variants with a prepended B<n_> like <n_not> generate a new target PMC.
If possible, they use the appropriate language type, specified with C<.HLL>.

=cut

###############################################################################

=head2 Flow control comparison operators

These operators branch based on the relationship between
their operands.

=over 4

=cut

########################################

=item B<eq>(in INT, in INT, labelconst INT)

=item B<eq>(in NUM, in NUM, labelconst INT)

=item B<eq>(in STR, in STR, labelconst INT)

=item B<eq>(invar PMC, invar PMC, labelconst INT)

=item B<eq>(invar PMC, in INT, labelconst INT)

=item B<eq>(invar PMC, in NUM, labelconst INT)

=item B<eq>(invar PMC, in STR, labelconst INT)

=item B<eq_str>(invar PMC, invar PMC, labelconst INT)

=item B<eq_num>(invar PMC, invar PMC, labelconst INT)

=item B<eq_addr>(in STR, in STR, labelconst INT)

=item B<eq_addr>(invar PMC, invar PMC, labelconst INT)

Branch if $1 is equal to $2. For PMCs this does a generic comparison,
whatever that is for the involved PMCs. Mixed PMC number/string
operands do a numeric or string comparison respectively.

B<eq_str> and B<eq_num> enforce string or number comparisons.

B<eq_addr> compares the address of the objects or strings.

=cut

inline op eq(in INT, in INT, labelconst INT) :base_core {
  if ($1 == $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

inline op eq(in NUM, in NUM, labelconst INT) :base_core {
  if ($1 == $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op eq(in STR, in STR, labelconst INT) :base_core {
  if (string_equal(interp, $1, $2) == 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op eq (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_EQ)) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op eq (invar PMC, in INT, labelconst INT) :base_core {
  /*
   * see also #34949
   * the temp is necessary, *if* $1 isa BigInt
   */
  PMC *temp = pmc_new(interp, enum_class_Integer);
  PMC_int_val(temp) = $2;
  if (mmd_dispatch_i_pp(interp, $1, temp, MMD_EQ)) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op eq (invar PMC, in NUM, labelconst INT) :base_core {
  /*
   * the get_number and get_string should probably
   * be also replaced with code like above, as
   * overriding the compare multi subs wouldn't
   * have any effect with the current code
   */
  if ($1->vtable->get_number(interp, $1) == $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op eq (invar PMC, in STR, labelconst INT) :base_core {
  if (0 == string_equal(interp,
      $1->vtable->get_string(interp, $1), $2)) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op eq_str (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_STREQ)) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op eq_num (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_NUMEQ)) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op eq_addr(in STR, in STR, labelconst INT) :base_core {
  if ($1 == $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op eq_addr(invar PMC, invar PMC, labelconst INT) :base_core {
  if ($1 == $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

########################################

=item B<ne>(in INT, in INT, labelconst INT)

=item B<ne>(in NUM, in NUM, labelconst INT)

=item B<ne>(in STR, in STR, labelconst INT)

=item B<ne>(invar PMC, invar PMC, labelconst INT)

=item B<ne>(invar PMC, in INT, labelconst INT)

=item B<ne>(invar PMC, in NUM, labelconst INT)

=item B<ne>(invar PMC, in STR, labelconst INT)

=item B<ne_str>(invar PMC, invar PMC, labelconst INT)

=item B<ne_num>(invar PMC, invar PMC, labelconst INT)

=item B<ne_addr>(in STR, in STR, labelconst INT)

=item B<ne_addr>(invar PMC, invar PMC, labelconst INT)

Branch if $1 is not equal to $2.

=cut

inline op ne(in INT, in INT, labelconst INT) :base_core {
  if ($1 != $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

inline op ne(in NUM, in NUM, labelconst INT) :base_core {
  if ($1 != $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ne(in STR, in STR, labelconst INT) :base_core {
  if (string_equal(interp, $1, $2) != 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ne(invar PMC, invar PMC, labelconst INT) :base_core {
  if (! mmd_dispatch_i_pp(interp, $1, $2, MMD_EQ)) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ne (invar PMC, in INT, labelconst INT) :base_core {
  PMC *temp = pmc_new(interp, enum_class_Integer);
  PMC_int_val(temp) = $2;
  if (! mmd_dispatch_i_pp(interp, $1, temp, MMD_EQ)) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ne (invar PMC, in NUM, labelconst INT) :base_core {
  if ($1->vtable->get_number(interp, $1) != $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ne (invar PMC, in STR, labelconst INT) :base_core {
  if (0 != string_equal(interp,
      $1->vtable->get_string(interp, $1), $2)) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ne_str (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_STRCMP) != 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ne_num (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_NUMCMP) != 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ne_addr(in STR, in STR, labelconst INT) :base_core {
  if ($1 != $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ne_addr(invar PMC, invar PMC, labelconst INT) :base_core {
  if ($1 != $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

########################################

=item B<lt>(in INT, in INT, labelconst INT)

=item B<lt>(in NUM, in NUM, labelconst INT)

=item B<lt>(in STR, in STR, labelconst INT)

=item B<lt>(invar PMC, invar PMC, labelconst INT)

=item B<lt>(invar PMC, in INT, labelconst INT)

=item B<lt>(invar PMC, in NUM, labelconst INT)

=item B<lt>(invar PMC, in STR, labelconst INT)

=item B<lt_str>(invar PMC, invar PMC, labelconst INT)

=item B<lt_num>(invar PMC, invar PMC, labelconst INT)

Branch if $1 is less than $2.

=cut

inline op lt(in INT, in INT, labelconst INT) :base_core {
  if ($1 < $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

inline op lt(in NUM, in NUM, labelconst INT) :base_core {
  if ($1 < $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op lt(in STR, in STR, labelconst INT) :base_core {
  if (string_compare(interp, $1, $2) < 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op lt (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_CMP) < 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op lt (invar PMC, in INT, labelconst INT) :base_core {
  PMC *temp = pmc_new(interp, enum_class_Integer);
  PMC_int_val(temp) = $2;
  if (mmd_dispatch_i_pp(interp, $1, temp, MMD_CMP) < 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op lt (invar PMC, in NUM, labelconst INT) :base_core {
  if ($1->vtable->get_number(interp, $1) < $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op lt (invar PMC, in STR, labelconst INT) :base_core {
  if (string_compare(interp,
      $1->vtable->get_string(interp, $1), $2) < 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op lt_str (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_STRCMP) < 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op lt_num (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_NUMCMP) < 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

########################################

=item B<le>(in INT, in INT, labelconst INT)

=item B<le>(in NUM, in NUM, labelconst INT)

=item B<le>(in STR, in STR, labelconst INT)

=item B<le>(invar PMC, invar PMC, labelconst INT)

=item B<le>(invar PMC, in INT, labelconst INT)

=item B<le>(invar PMC, in NUM, labelconst INT)

=item B<le>(invar PMC, in STR, labelconst INT)

=item B<le_str>(invar PMC, invar PMC, labelconst INT)

=item B<le_num>(invar PMC, invar PMC, labelconst INT)

Branch if $1 is less than or equal to $2.

=cut

inline op le(in INT, in INT, labelconst INT) :base_core {
  if ($1 <= $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

inline op le(in NUM, in NUM, labelconst INT) :base_core {
  if ($1 <= $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op le(in STR, in STR, labelconst INT) :base_core {
  if (string_compare(interp, $1, $2) <= 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op le(invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_CMP) <= 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op le(invar PMC, in INT, labelconst INT) :base_core {
  PMC *temp = pmc_new(interp, enum_class_Integer);
  PMC_int_val(temp) = $2;
  if (mmd_dispatch_i_pp(interp, $1, temp, MMD_CMP) <= 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op le (invar PMC, in NUM, labelconst INT) :base_core {
  if ($1->vtable->get_number(interp, $1) <= $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op le (invar PMC, in STR, labelconst INT) :base_core {
  if (string_compare(interp,
      $1->vtable->get_string(interp, $1), $2) <= 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op le_str (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_STRCMP) <= 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op le_num (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_NUMCMP) <= 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

########################################

=item B<gt>(invar PMC, invar PMC, labelconst INT)

=item B<gt>(invar PMC, in INT, labelconst INT)

=item B<gt>(invar PMC, in NUM, labelconst INT)

=item B<gt>(invar PMC, in STR, labelconst INT)

=item B<gt_str>(invar PMC, invar PMC, labelconst INT)

=item B<gt_num>(invar PMC, invar PMC, labelconst INT)

Branch if $1 is greater than $2.

=cut

op gt(invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_CMP) > 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op gt(invar PMC, in INT, labelconst INT) :base_core {
  PMC *temp = pmc_new(interp, enum_class_Integer);
  PMC_int_val(temp) = $2;
  if (mmd_dispatch_i_pp(interp, $1, temp, MMD_CMP) > 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op gt (invar PMC, in NUM, labelconst INT) :base_core {
  if ($1->vtable->get_number(interp, $1) > $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op gt (invar PMC, in STR, labelconst INT) :base_core {
  if (string_compare(interp,
      $1->vtable->get_string(interp, $1), $2) > 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op gt_str (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_STRCMP) > 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op gt_num (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_NUMCMP) > 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

########################################

=item B<ge>(invar PMC, invar PMC, labelconst INT)

=item B<ge>(invar PMC, in INT, labelconst INT)

=item B<ge>(invar PMC, in NUM, labelconst INT)

=item B<ge>(invar PMC, in STR, labelconst INT)

=item B<ge_str>(invar PMC, invar PMC, labelconst INT)

=item B<ge_num>(invar PMC, invar PMC, labelconst INT)

Branch if $1 is greater than or equal to $2.

=cut

op ge(invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_CMP) >= 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ge(invar PMC, in INT, labelconst INT) :base_core {
  PMC *temp = pmc_new(interp, enum_class_Integer);
  PMC_int_val(temp) = $2;
  if (mmd_dispatch_i_pp(interp, $1, temp, MMD_CMP) >= 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ge (invar PMC, in NUM, labelconst INT) :base_core {
  if ($1->vtable->get_number(interp, $1) >= $2) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ge (invar PMC, in STR, labelconst INT) :base_core {
  if (string_compare(interp,
      $1->vtable->get_string(interp, $1), $2) >= 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ge_str (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_STRCMP) >= 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

op ge_num (invar PMC, invar PMC, labelconst INT) :base_core {
  if (mmd_dispatch_i_pp(interp, $1, $2, MMD_NUMCMP) >= 0) {
    goto OFFSET($3);
  }
  goto NEXT();
}

########################################

=item B<if_null>(invar PMC, labelconst INT)

Branch to $2 if $1 is a NULL PMC.

=item B<if_null>(invar STR, labelconst INT)

Branch to $2 if $1 is a NULL STRING.

=cut

op if_null(invar PMC, labelconst INT) {
  if (PMC_IS_NULL($1)) {
    goto OFFSET($2);
  }
  goto NEXT();
}

op if_null(invar STR, labelconst INT) {
  if (!$1) {
    goto OFFSET($2);
  }
  goto NEXT();
}

########################################

=item B<unless_null>(invar PMC, labelconst INT)

Branch to $2 if $1 is I<not> a NULL PMC.

=item B<unless_null>(invar STR, labelconst INT)

Branch to $2 if $1 is I<not> a NULL STRING.

=cut

op unless_null(invar PMC, labelconst INT) {
  if (!PMC_IS_NULL($1)) {
    goto OFFSET($2);
  }
  goto NEXT();
}

op unless_null(invar STR, labelconst INT) {
  if ($1) {
    goto OFFSET($2);
  }
  goto NEXT();
}

=back

=cut


###############################################################################

=head2 Non-flow control comparison ops

These ops do comparisons and truth testing without doing flow control.

=over 4

=item B<cmp>(out INT, in INT, in INT)

=item B<cmp>(out INT, in NUM, in NUM)

=item B<cmp>(out INT, in STR, in STR)

=item B<cmp>(out INT, invar PMC, invar PMC)

=item B<cmp>(out INT, invar PMC, in INT)

=item B<cmp>(out INT, invar PMC, in NUM)

=item B<cmp>(out INT, invar PMC, in STR)

=item B<cmp_str>(out INT, invar PMC, invar PMC)

=item B<cmp_num>(out INT, invar PMC, invar PMC)


Sets $1 to -1 if $2 < $3, +1 if $2 > $3, and 0 otherwise.

=cut

inline op cmp(out INT, in INT, in INT) :base_core {
  $1 = $2 < $3 ? -1 :
       $2 > $3 ? +1 :
       0;
  goto NEXT();
}

inline op cmp(out INT, in NUM, in NUM) :base_core {
  $1 = $2 < $3 ? -1 :
       $2 > $3 ? +1 :
       0;
  goto NEXT();
}

inline op cmp(out INT, in STR, in STR) :base_core {
  $1 = string_compare(interp, $2, $3);
  goto NEXT();
}

inline op cmp(out INT, invar PMC, invar PMC) :base_core {
  $1 = mmd_dispatch_i_pp(interp, $2, $3, MMD_CMP);
  goto NEXT();
}

inline op cmp(out INT, invar PMC, in INT) :base_core {
  INTVAL l = VTABLE_get_integer(interp, $2);
  $1 = l < $3 ? -1 :
       l > $3 ? +1 :
       0;
  goto NEXT();
}

inline op cmp(out INT, invar PMC, in NUM) :base_core {
  FLOATVAL l = VTABLE_get_number(interp, $2);
  $1 = l < $3 ? -1 :
       l > $3 ? +1 :
       0;
  goto NEXT();
}

inline op cmp(out INT, invar PMC, in STR) :base_core {
  STRING* l = VTABLE_get_string(interp, $2);
  $1 = string_compare(interp, l, $3);
  goto NEXT();
}

inline op cmp_str(out INT, invar PMC, invar PMC) :base_core {
  $1 = mmd_dispatch_i_pp(interp, $2, $3, MMD_STRCMP);
  goto NEXT();
}

inline op cmp_num(out INT, invar PMC, invar PMC) :base_core {
  $1 = mmd_dispatch_i_pp(interp, $2, $3, MMD_NUMCMP);
  goto NEXT();
}

########################################

=item B<issame>(out INT, invar PMC, invar PMC)

=item B<issame>(out INT, in STR, in STR)

Sets $1 to 1 or 0, depending on the identity of the 2 objects.
The identity is the PMCs memory address.

=item B<isntsame>(out INT, invar PMC, invar PMC)

=item B<isntsame>(out INT, in STR, in STR)

Sets $1 to 0 or 1, depending on the identity of the 2 objects.

=item B<istrue>(out INT, invar PMC)

Sets $1 to 1 or 0, depending on whether $2 is true or not.

=item B<isfalse>(out INT, invar PMC)

Sets $1 to 1 or 0, depending on whether $2 is false or not.

=item B<isnull>(out INT, in PMC)

Sets $1 to 1 if the object is null, 0 otherwise.

=cut

inline op issame(out INT, invar PMC, invar PMC) {
    if ($2 == $3)
        $1 = 1;
    else
        $1 = VTABLE_is_same(interp, $2, $3);
    goto NEXT();
}

inline op issame(out INT, in STR, in STR) {
    $1 = $2 == $3;
    goto NEXT();
}

inline op isntsame(out INT, invar PMC, invar PMC) {
    if ($2 == $3)
        $1 = 0;
    else
        $1 = !VTABLE_is_same(interp, $2, $3);
    goto NEXT();
}

inline op isntsame(out INT, in STR, in STR) {
    $1 = $2 != $3;
    goto NEXT();
}

inline op istrue(out INT, invar PMC) {
    $1 = VTABLE_get_bool(interp, $2);
    goto NEXT();
}

inline op isfalse(out INT, invar PMC) {
    $1 = !VTABLE_get_bool(interp, $2);
    goto NEXT();
}

inline op isnull(out INT, in PMC) {
    $1 = PMC_IS_NULL($2);
    goto NEXT();
}

=item B<isgt>(out INT, in INT, in INT)

=item B<isgt>(out INT, in NUM, in NUM)

=item B<isgt>(out INT, in STR, in STR)

These 3 opcodes are valid, but create as their reverse.

=item B<isgt>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is greater than $3.

=cut

inline op isgt(out INT, invar PMC, invar PMC) {
  $1 = (mmd_dispatch_i_pp(interp, $2, $3, MMD_CMP) > 0);
  goto NEXT();
}

=item B<isge>(out INT, in INT, in INT)

=item B<isge>(out INT, in NUM, in NUM)

=item B<isge>(out INT, in STR, in STR)

These 3 opcodes are valid, but create as their reverse.

=item B<isge>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is greater than or equal to $3.

=cut

inline op isge(out INT, invar PMC, invar PMC) {
  $1 = (mmd_dispatch_i_pp(interp, $2, $3, MMD_CMP) >= 0);
  goto NEXT();
}

=item B<isle>(out INT, in INT, in INT)

=item B<isle>(out INT, in NUM, in NUM)

=item B<isle>(out INT, in STR, in STR)

=item B<isle>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is less than or equal to $3.

=cut

inline op isle(out INT, in INT, in INT) {
  $1 = $2 <= $3;
  goto NEXT();
}

inline op isle(out INT, in NUM, in NUM) {
  $1 = $2 <= $3;
  goto NEXT();
}

inline op isle(out INT, in STR, in STR) {
  $1 = string_compare(interp, $2, $3) <= 0;
  goto NEXT();
}

inline op isle(out INT, invar PMC, invar PMC) {
  $1 = (mmd_dispatch_i_pp(interp, $2, $3, MMD_CMP) <= 0);
  goto NEXT();
}

=item B<islt>(out INT, in INT, in INT)

=item B<islt>(out INT, in NUM, in NUM)

=item B<islt>(out INT, in STR, in STR)

=item B<islt>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is less than $3.

=cut

inline op islt(out INT, in INT, in INT) {
  $1 = ($2 < $3) ? 1 : 0;
  goto NEXT();
}

inline op islt(out INT, in NUM, in NUM) {
  $1 = $2 < $3;
  goto NEXT();
}

inline op islt(out INT, in STR, in STR) {
  $1 = string_compare(interp, $2, $3) < 0;
  goto NEXT();
}

inline op islt(out INT, invar PMC, invar PMC) {
  $1 = (mmd_dispatch_i_pp(interp, $2, $3, MMD_CMP) < 0);
  goto NEXT();
}

=item B<iseq>(out INT, in INT, in INT)

=item B<iseq>(out INT, in NUM, in NUM)

=item B<iseq>(out INT, in STR, in STR)

=item B<iseq>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is equal to $3.

=cut

inline op iseq(out INT, in INT, in INT) {
  $1 = ($2 == $3) ? 1 : 0;
  goto NEXT();
}

inline op iseq(out INT, in NUM, in NUM) {
  $1 = $2 == $3;
  goto NEXT();
}

inline op iseq(out INT, in STR, in STR) {
  $1 = string_equal(interp, $2, $3) == 0;
  goto NEXT();
}

inline op iseq(out INT, invar PMC, invar PMC) {
  if (&$2 == &$3)
    $1 = 1;
  else
    $1 = mmd_dispatch_i_pp(interp, $2, $3, MMD_EQ);
  goto NEXT();
}

=item B<isne>(out INT, in INT, in INT)

=item B<isne>(out INT, in NUM, in NUM)

=item B<isne>(out INT, in STR, in STR)

=item B<isne>(out INT, invar PMC, invar PMC)

Sets $1 to 1 if $2 is not equal to $3.

=cut

inline op isne(out INT, in INT, in INT) {
  $1 = ($2 == $3) ? 0 : 1;
  goto NEXT();
}

inline op isne(out INT, in NUM, in NUM) {
  $1 = $2 != $3;
  goto NEXT();
}

inline op isne(out INT, in STR, in STR) {
  $1 = string_equal(interp, $2, $3) != 0;
  goto NEXT();
}

inline op isne(out INT, invar PMC, invar PMC) {
  if (&$2 == &$3)
    $1 = 0;
  else
    $1 = !mmd_dispatch_i_pp(interp, $2, $3, MMD_EQ);
  goto NEXT();
}

=back

=cut


###############################################################################

=head2 Logical operations

These operations apply logical functions to their arguments.

=over 4

=cut

########################################

=item B<and>(out INT, in INT, in INT)

Short-circuiting logical and.  Returns $2 if it's false, else returns $3.

=cut

inline op and(out INT, in INT, in INT) :base_core {
  $1 = $2 ? $3 : $2;
  goto NEXT();
}

########################################

=item B<not>(inout INT)

=item B<not>(invar PMC)

Set the boolean state of $1 to the opposite of the boolean state from $1.

=item B<not>(out INT, in INT)

=item B<not>(out PMC, invar PMC)

=item B<n_not>(out PMC, invar PMC)

Set the boolean state of $1 to the opposite of the boolean state from $2.

=cut

inline op not(inout INT) :base_core {
  $1 = ! $1;
  goto NEXT();
}

inline op not(out INT, in INT) :base_core {
  $1 = ! $2;
  goto NEXT();
}

inline op not(invar PMC) :base_core {
  $2->vtable->i_logical_not(interp, $1);
  goto NEXT();
}

inline op not(out PMC, invar PMC) :base_core {
  $1 = $2->vtable->logical_not(interp, $2, $1);
  goto NEXT();
}

inline op n_not(out PMC, invar PMC) :base_core {
  $1 = $2->vtable->logical_not(interp, $2, NULL);
  goto NEXT();
}

########################################

=item B<or>(out INT, in INT, in INT)

Short-circuiting logical or.  Returns $2 if it's true, else returns $3.

=cut

inline op or(out INT, in INT, in INT) :base_core {
  $1 = $2 ? $2 : $3;
  goto NEXT();
}

########################################

=item B<xor>(out INT, in INT, in INT)

Logical xor.  Returns $2 if it's true and $3 isn't,
returns $3 if it's true and $2 isn't, else returns false.

=cut

inline op xor(out INT, in INT, in INT) :base_core {
  $1 = ($2 && ! $3) ? $2 : ($3 && ! $2) ? $3 : 0;
  goto NEXT();
}

=back

=cut


###############################################################################

=head1 COPYRIGHT

Copyright (C) 2001-2004, The Perl Foundation.

=head1 LICENSE

This program is free software. It is subject to the same license
as the Parrot interpreter itself.

=cut

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
