/*
Copyright (C) 2005-2008, The Perl Foundation.
$Id: /mirror/trunk/languages/lua/src/pmc/luastring.pmc 35819 2009-01-20T05:19:27.074079Z chromatic  $

=head1 NAME

pmc/luastring.pmc - Lua String

=head1 DESCRIPTION

C<LuaString> extends C<LuaAny> to provide a class with the behaviour of
the Lua C<String> type.

=head2 Overloaded Methods

=over 4

=cut

*/

#include "lua_private.h"
#include "pmc_luanumber.h"


pmclass LuaString
    extends  LuaAny
    provides scalar
    provides string
    dynpmc
    group    lua_group
    hll      lua
    maps     String {

/*

=item C<void init()>

Initializes the string.

=cut

*/
    VTABLE void init() {
        PMC_str_val(SELF) = string_make_empty(INTERP, enum_stringrep_one, 0);
        PObj_custom_mark_SET(SELF);
    }

/*

=item C<PMC* instantiate_str(STRING *rep, INTVAL flags)>

Allow :

    .const .LuaString s = "Hello World!"

=cut

*/
    VTABLE PMC* instantiate_str(STRING *rep, INTVAL flags) {
        PMC *res;
        INTVAL type = PMC_type(SELF);
        if (flags & PObj_constant_FLAG)
            res = constant_pmc_new(INTERP, type);
        else
            res = pmc_new(INTERP, type);

        VTABLE_set_string_native(INTERP, res, string_copy(INTERP, rep));
        PObj_custom_mark_SET(res);
        return res;
    }

/*

=item C<void mark()>

Marks the string as live.

=cut

*/
    VTABLE void mark() {
        if (PMC_str_val(SELF))
            pobject_lives(INTERP, (PObj *)PMC_str_val(SELF));
    }

/*

=item C<PMC* clone()>

Creates a copy of the string.

=cut

*/
    VTABLE PMC* clone() {
        return VTABLE_instantiate_str(INTERP, SELF, PMC_str_val(SELF), 0);
    }

/*

=item C<STRING* get_string()>

Returns the string itself.

=cut

*/
    VTABLE STRING* get_string() {
        return PMC_str_val(SELF);
    }

/*

=item C<STRING* name()>

Return the string "string".

=cut

*/
    VTABLE STRING* name() {
        return const_string(INTERP, "string");
    }

/*

=item C<void set_integer_native(INTVAL value)>

=cut

*/
    VTABLE void set_integer_native(INTVAL value) {
        SELF.morph(dynpmc_LuaNumber);
        SELF.set_integer_native(value);
    }

/*

=item C<void set_number_native(FLOATVAL value)>

=cut

*/
    VTABLE void set_number_native(FLOATVAL value) {
        SELF.morph(dynpmc_LuaNumber);
        SELF.set_number_native(value);
    }

/*

=item C<VOID set_string_native(STRING *value)>

Sets the value of the string to that of the specified C<string>.

=cut

*/
    VTABLE void set_string_native(STRING *value) {
        /* Only allow constant PMCs to embed constant strings */
        if (PObj_constant_TEST(SELF) && !PObj_constant_TEST(value)) {
            const char *copy = string_to_cstring(INTERP, value);
            value            = const_string(INTERP, copy);
        }

        PMC_str_val(SELF) = value;
    }

/*

=item C<VOID set_pmc(PMC *value)>

Sets the value of the string to the string value of
the specified C<PMC>.

=cut

*/
    VTABLE void set_pmc(PMC *value) {
        PMC_str_val(SELF) = VTABLE_get_string(INTERP, value);
    }

/*

=item C<PMC* neg(PMC *dest)>

=cut

*/
    VTABLE PMC* neg(PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return LuaNumber.n.neg(dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

/*

=item C<void i_neg()>

=cut

*/
    VTABLE void i_neg() {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            LuaNumber.n.i_neg();
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

/*

=item C<INTVAL elements()>

Return length of the string.

=cut

*/
    VTABLE INTVAL elements() {
        return string_length(INTERP, PMC_str_val(SELF));
    }

/*

=item C<void freeze(visit_info *info)>

Used to archive the string.

=cut

*/
    VTABLE void freeze(visit_info *info) {
        IMAGE_IO *io = info->image_io;
        SUPER(info);
        VTABLE_push_string(INTERP, io, PMC_str_val(SELF));
    }

/*

=item C<void thaw(visit_info *info)>

Used to unarchive the string.

=cut

*/
    VTABLE void thaw(visit_info *info) {
        IMAGE_IO *io = info->image_io;
        SUPER(info);
        if (info->extra_flags == EXTRA_IS_NULL)
            PMC_str_val(SELF) = VTABLE_shift_string(INTERP, io);
    }

/*

=back

=head2 non-Vtable Methods

=over 4

=item C<PMC* add(PMC *value, PMC *dest)>

=cut

*/
    MULTI PMC* add(LuaNumber value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_add_LuaNumber_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* add(LuaString value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_add_LuaString_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* add(DEFAULT value, PMC *dest) {
        PMC *meth = find_meth(INTERP, value, "__add");
        if (meth) {
            dest = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(dest))
                dest = pmc_new(INTERP, dynpmc_LuaNil);

            return dest;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<void i_add(PMC *value)>

=cut

*/
    MULTI void i_add(LuaNumber value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_add_LuaNumber(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_add(LuaString value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_add_LuaString(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_add(DEFAULT value) {
        PMC *meth = find_meth(INTERP, value, "__add");
        if (meth) {
            SELF = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(SELF))
                SELF = pmc_new(INTERP, dynpmc_LuaNil);

            return;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<PMC* subtract(PMC *value, PMC *dest)>

=cut

*/
    MULTI PMC* subtract(LuaNumber value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_subtract_LuaNumber_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* subtract(LuaString value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_subtract_LuaString_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* subtract(DEFAULT value, PMC *dest) {
        PMC *meth = find_meth(INTERP, value, "__sub");
        if (meth) {
            dest = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(dest))
                dest = pmc_new(INTERP, dynpmc_LuaNil);

            return dest;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<void i_subtract(PMC *value)>

=cut

*/
    MULTI void i_subtract(LuaNumber value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_subtract_LuaNumber(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_subtract(LuaString value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_subtract_LuaString(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_subtract(DEFAULT value) {
        PMC *meth = find_meth(INTERP, value, "__sub");

        if (meth) {
            SELF = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(SELF))
                SELF = pmc_new(INTERP, dynpmc_LuaNil);

            return;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<PMC* multiply(PMC *value, PMC *dest)>

=cut

*/
    MULTI PMC* multiply(LuaNumber value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_multiply_LuaNumber_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* multiply(LuaString value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_multiply_LuaString_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* multiply(DEFAULT value, PMC *dest) {
        PMC *meth = find_meth(INTERP, value, "__mul");
        if (meth) {
            dest = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(dest))
                dest = pmc_new(INTERP, dynpmc_LuaNil);

            return dest;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<void i_multiply(PMC *value)>

=cut

*/
    MULTI void i_multiply(LuaNumber value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_multiply_LuaNumber(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_multiply(LuaString value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_multiply_LuaString(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_multiply(DEFAULT value) {
        PMC *meth = find_meth(INTERP, value, "__mul");
        if (meth) {
            SELF = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(SELF))
                SELF = pmc_new(INTERP, dynpmc_LuaNil);

            return;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<PMC* divide(PMC *value, PMC *dest)>

=cut

*/
    MULTI PMC* divide(LuaNumber value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_divide_LuaNumber_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* divide(LuaString value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_divide_LuaString_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* divide(DEFAULT value, PMC *dest) {
        PMC *meth = find_meth(INTERP, value, "__div");

        if (meth) {
            dest = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(dest))
                dest = pmc_new(INTERP, dynpmc_LuaNil);

            return dest;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<void i_divide(PMC *value)>

=cut

*/
    MULTI void i_divide(LuaNumber value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_divide_LuaNumber(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_divide(LuaString value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_divide_LuaString(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_divide(DEFAULT value) {
        PMC *meth = find_meth(INTERP, value, "__div");
        if (meth) {
            SELF = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(SELF))
                SELF = pmc_new(INTERP, dynpmc_LuaNil);

            return;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<PMC* modulus(PMC *value, PMC *dest)>

=cut

*/
    MULTI PMC* modulus(LuaNumber value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_modulus_LuaNumber_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* modulus(LuaString value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_modulus_LuaString_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* modulus(DEFAULT value, PMC *dest) {
        PMC *meth = find_meth(INTERP, value, "__mod");

        if (meth) {
            dest = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(dest))
                dest = pmc_new(INTERP, dynpmc_LuaNil);

            return dest;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<void i_modulus(PMC *value)>

=cut

*/
    MULTI void i_modulus(LuaNumber value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_modulus_LuaNumber(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_modulus(LuaString value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_modulus_LuaString(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_modulus(DEFAULT value) {
        PMC *meth = find_meth(INTERP, value, "__mod");
        if (meth) {
            SELF = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(SELF))
                SELF = pmc_new(INTERP, dynpmc_LuaNil);

            return;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<PMC* pow(PMC *value, PMC *dest)>

=cut

*/
    MULTI PMC* pow(LuaNumber value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_pow_LuaNumber_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* pow(LuaString value, PMC *dest) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            return Parrot_LuaNumber_multi_pow_LuaString_PMC(INTERP, n, value, dest);

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value", SELF.name());
    }

    MULTI PMC* pow(DEFAULT value, PMC *dest) {
        PMC *meth = find_meth(INTERP, value, "__pow");

        if (meth) {
            dest = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(dest))
                dest = pmc_new(INTERP, dynpmc_LuaNil);

            return dest;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<void i_pow(PMC *value)>

=cut

*/
    MULTI void i_pow(LuaNumber value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_pow_LuaNumber(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_pow(LuaString value) {
        PMC *n;
        Parrot_PCCINVOKE(interp, SELF, const_string(interp, "tonumber"),
            "->P", &n);

        if (PMC_type(n) == dynpmc_LuaNumber)
            Parrot_LuaNumber_multi_i_pow_LuaString(INTERP, n, value);
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "attempt to perform arithmetic on a %Ss value",
                    SELF.name());
    }

    MULTI void i_pow(DEFAULT value) {
        PMC *meth = find_meth(INTERP, value, "__pow");

        if (meth) {
            SELF = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(SELF))
                SELF = pmc_new(INTERP, dynpmc_LuaNil);

            return;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to perform arithmetic on a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<INTVAL is_equal(PMC *value)>

Compares the string with C<value>; returns true if
they match.

=cut

*/
    MULTI INTVAL is_equal(LuaString value) {
        STRING *s = PMC_str_val(SELF);
        STRING *v = VTABLE_get_string(INTERP, value);
        return (INTVAL)(0 == string_equal(INTERP, s, v));
    }

    MULTI INTVAL is_equal(DEFAULT value) {
        return (INTVAL)0;
    }

/*

=item C<INTVAL cmp(PMC *value)>

Compares the string with C<value>; returns -1 if the
string is smaller, 0 if they are equal, and 1 if C<value>
is smaller.

Throws an exception unless C<value> is a LuaString.

=cut

*/
    MULTI INTVAL cmp(LuaString value) {
        STRING *s = PMC_str_val(SELF);
        STRING *v = VTABLE_get_string(INTERP, value);
        return string_compare(INTERP, s, v);
    }

    MULTI INTVAL cmp(DEFAULT value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to compare string with %Ss",
                VTABLE_name(INTERP, value));
    }

/*

=item C<PMC* concatenate(PMC *value, PMC *dest)>

=cut

*/
    MULTI PMC* concatenate(LuaNumber value, PMC *dest) {
        STRING *s = string_concat(INTERP,
            SELF.get_string(), VTABLE_get_string(INTERP, value), 0);

        dest = pmc_new(INTERP, dynpmc_LuaString);
        VTABLE_set_string_native(INTERP, dest, s);
        return dest;
    }

    MULTI PMC* concatenate(LuaString value, PMC *dest) {
        STRING *s = string_concat(INTERP,
            SELF.get_string(), VTABLE_get_string(INTERP, value), 0);

        dest = pmc_new(INTERP, dynpmc_LuaString);
        VTABLE_set_string_native(INTERP, dest, s);
        return dest;
    }

    MULTI PMC* concatenate(DEFAULT value, PMC *dest) {
        PMC *meth = find_meth(INTERP, value, "__concat");

        if (meth) {
            dest = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(dest))
                dest = pmc_new(INTERP, dynpmc_LuaNil);

            return dest;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to concatenate a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=item C<void i_concatenate(PMC *value)>

=cut

*/
    MULTI void i_concatenate(LuaNumber value) {
        STRING *s = SELF.get_string();
        STRING *v = VTABLE_get_string(INTERP, value);
        SELF.set_string_native(string_append(INTERP, s, v));
    }

    MULTI void i_concatenate(LuaString value) {
        STRING *s = SELF.get_string();
        STRING *v = VTABLE_get_string(INTERP, value);
        SELF.set_string_native(string_append(INTERP, s, v));
    }

    MULTI void i_concatenate(DEFAULT value) {
        PMC *meth = find_meth(INTERP, value, "__concat");

        if (meth) {
            SELF = Parrot_runops_fromc_args(INTERP, meth, "PPP", SELF, value);
            if (PMC_IS_NULL(SELF))
                SELF = pmc_new(INTERP, dynpmc_LuaNil);

            return;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
            "attempt to concatenate a %Ss value",
                VTABLE_name(INTERP, value));
    }

/*

=back

=head2 Specific Methods

=over 4

=item C<PMC *get_metatable()>

=cut

*/
    METHOD PMC* get_metatable() {
        PMC *retval = Parrot_find_global_s(INTERP,
                             const_string(INTERP, "string"),
                             const_string(INTERP, "mt_string"));
        if (!retval)
            retval = pmc_new(INTERP, dynpmc_LuaNil);

        RETURN(PMC *retval);
    }

/*

=item C<PMC* len()>


=cut

*/
    METHOD PMC* len() {
        PMC *retval         = pmc_new(INTERP, dynpmc_LuaNumber);
        VTABLE_set_number_native(INTERP, retval, SELF.elements());
        RETURN(PMC *retval);
    }

/*

=item C<PMC* rawequal(PMC *value)>

=cut

*/
    METHOD PMC* rawequal(PMC *value) {
        PMC *retval = pmc_new(INTERP, dynpmc_LuaBoolean);

        if (PMC_type(SELF) == PMC_type(value)
        && 0               == string_equal(INTERP, PMC_str_val(SELF),
                                  VTABLE_get_string(INTERP, value)))
            VTABLE_set_bool(INTERP, retval, 1);
        else
            VTABLE_set_bool(INTERP, retval, 0);

        RETURN(PMC *retval);
    }

/*

=item C<PMC* tonumber()>

=cut

*/
    METHOD PMC* tonumber() {
        PMC     *retval;
        STRING  *rep = SELF.get_string();
        char    *s1  = string_to_cstring(INTERP, rep);
        char    *s2;
        FLOATVAL d   = strtod(s1, &s2);

        /* at least one valid digit? */
        if (s1 != s2) {
            /* skip trailing spaces */
            while (isspace((unsigned char)(*s2)))
                s2++;

            /* no invalid trailing characters? */
            if (*s2 == '\0') {
                retval = pmc_new(INTERP, dynpmc_LuaNumber);
                VTABLE_set_number_native(INTERP, retval, d);
                string_cstring_free(s1);
                RETURN(PMC *retval);
            }
        }
        retval = pmc_new(INTERP, dynpmc_LuaNil);
        string_cstring_free(s1);
        RETURN(PMC *retval);
    }

/*

=item C<PMC* tobase(INTVAL base)>

=cut

*/
    METHOD PMC* tobase(INTVAL base) {
        PMC          *retval;
        STRING       *rep = SELF.get_string();
        char         *s1  = string_to_cstring(INTERP, rep);
        char         *s2;
        unsigned long n   = strtoul(s1, &s2, base);

        /* at least one valid digit? */
        if (s1 != s2) {

            /* skip trailing spaces */
            while (isspace((unsigned char)(*s2)))
                s2++;

            /* no invalid trailing characters? */
            if (*s2 == '\0') {
                retval = pmc_new(INTERP, dynpmc_LuaNumber);
                VTABLE_set_number_native(INTERP, retval, n);
                string_cstring_free(s1);
                RETURN(PMC *retval);
            }
        }
        retval = pmc_new(INTERP, dynpmc_LuaNil);
        string_cstring_free(s1);
        RETURN(PMC *retval);
    }

}

/*

=back

=cut

*/

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