// vim: ts=4 sw=4 expandtab ft=c

// Copyright (C) 2002-2007, 2011 The University of Melbourne.
// Copyright (C) 2013-2018 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.

// mercury_deconstruct.c
//
// This file provides utility functions for deconstructing terms, for use by
// the standard library.

#include "mercury_imp.h"
#include "mercury_deconstruct.h"
#include "mercury_deconstruct_macros.h"
#include "mercury_type_desc.h"
#include "mercury_minimal_model.h"

// We reserve a buffer to hold the names we dynamically generate
// for "functors" of foreign types. This macro gives its size.

#define MR_FOREIGN_NAME_BUF_SIZE    256

static  MR_ConstString  MR_expand_type_name(MR_TypeCtorInfo tci, MR_bool);

#define EXPAND_FUNCTION_NAME        MR_expand_functor_args
#define EXPAND_TYPE_NAME            MR_ExpandFunctorArgsInfo
#define EXPAND_FUNCTOR_FIELD        functor
#define EXPAND_ARGS
#include "mercury_ml_expand_body.h"
#undef  EXPAND_FUNCTION_NAME
#undef  EXPAND_TYPE_NAME
#undef  EXPAND_FUNCTOR_FIELD
#undef  EXPAND_ARGS

#define EXPAND_FUNCTION_NAME        MR_expand_functor_args_limit
#define EXPAND_TYPE_NAME            MR_ExpandFunctorArgsLimitInfo
#define EXPAND_FUNCTOR_FIELD        functor
#define EXPAND_ARGS
#define EXPAND_APPLY_LIMIT
#include "mercury_ml_expand_body.h"
#undef  EXPAND_FUNCTION_NAME
#undef  EXPAND_TYPE_NAME
#undef  EXPAND_FUNCTOR_FIELD
#undef  EXPAND_ARGS
#undef  EXPAND_APPLY_LIMIT

#define EXPAND_FUNCTION_NAME        MR_expand_functor_only
#define EXPAND_TYPE_NAME            MR_ExpandFunctorOnlyInfo
#define EXPAND_FUNCTOR_FIELD        functor_only
#include "mercury_ml_expand_body.h"
#undef  EXPAND_FUNCTION_NAME
#undef  EXPAND_TYPE_NAME
#undef  EXPAND_FUNCTOR_FIELD

#define EXPAND_FUNCTION_NAME        MR_expand_args_only
#define EXPAND_TYPE_NAME            MR_ExpandArgsOnlyInfo
#define EXPAND_ARGS
#include "mercury_ml_expand_body.h"
#undef  EXPAND_FUNCTION_NAME
#undef  EXPAND_TYPE_NAME
#undef  EXPAND_ARGS

#define EXPAND_FUNCTION_NAME        MR_expand_chosen_arg_only
#define EXPAND_TYPE_NAME            MR_ExpandChosenArgOnlyInfo
#define EXPAND_CHOSEN_ARG
#include "mercury_ml_expand_body.h"
#undef  EXPAND_FUNCTION_NAME
#undef  EXPAND_TYPE_NAME
#undef  EXPAND_CHOSEN_ARG

#define EXPAND_FUNCTION_NAME        MR_expand_named_arg_only
#define EXPAND_TYPE_NAME            MR_ExpandChosenArgOnlyInfo
#define EXPAND_NAMED_ARG
#include "mercury_ml_expand_body.h"
#undef  EXPAND_FUNCTION_NAME
#undef  EXPAND_TYPE_NAME
#undef  EXPAND_NAMED_ARG

// N.B. any modifications to the signature of this function will require
// changes not only to library/deconstruct.m, but also to library/store.m
// and extras/trailed_update/tr_store.m.

MR_bool
MR_arg(MR_TypeInfo type_info, MR_Word *term_ptr,
    MR_noncanon_handling noncanon, int arg_index,
    MR_TypeInfo *arg_type_info_ptr, MR_Word *arg_term_ptr,
    MR_Word **word_sized_arg_ptr)
{
    MR_ExpandChosenArgOnlyInfo  expand_info;

    MR_expand_chosen_arg_only(type_info, term_ptr, noncanon, arg_index,
        &expand_info);

    if (expand_info.chosen_index_exists) {
        *arg_type_info_ptr = expand_info.chosen_arg_type_info;
        *arg_term_ptr = expand_info.chosen_arg_term;
        *word_sized_arg_ptr = expand_info.chosen_arg_word_sized_ptr;
        return MR_TRUE;
    }

    return MR_FALSE;
}

MR_bool
MR_named_arg(MR_TypeInfo type_info, MR_Word *term_ptr,
    MR_noncanon_handling noncanon, MR_ConstString arg_name,
    MR_TypeInfo *arg_type_info_ptr, MR_Word *arg_term_ptr,
    MR_Word **word_sized_arg_ptr)
{
    MR_ExpandChosenArgOnlyInfo  expand_info;

    MR_expand_named_arg_only(type_info, term_ptr, noncanon, arg_name,
        &expand_info);

    if (expand_info.chosen_index_exists) {
        *arg_type_info_ptr = expand_info.chosen_arg_type_info;
        *arg_term_ptr = expand_info.chosen_arg_term;
        *word_sized_arg_ptr = expand_info.chosen_arg_word_sized_ptr;
        return MR_TRUE;
    }

    return MR_FALSE;
}

MR_bool
MR_named_arg_num(MR_TypeInfo type_info, MR_Word *term_ptr,
    const char *arg_name, int *arg_num_ptr)
{
    MR_TypeCtorInfo             type_ctor_info;
    const MR_DuPtagLayout       *ptag_layout;
    const MR_DuFunctorDesc      *functor_desc;
    const MR_NotagFunctorDesc   *notag_functor_desc;
    MR_Word                     data;
    int                         ptag;
    MR_Word                     sectag;
    MR_TypeInfo                 eqv_type_info;
    int                         i;

    type_ctor_info = MR_TYPEINFO_GET_TYPE_CTOR_INFO(type_info);

    if (! MR_type_ctor_has_valid_rep(type_ctor_info)) {
        MR_fatal_error("MR_named_arg_num: term of unknown representation");
    }

    switch (MR_type_ctor_rep(type_ctor_info)) {
        case MR_TYPECTOR_REP_DU_USEREQ:
        case MR_TYPECTOR_REP_DU:
            data = *term_ptr;

            ptag = MR_tag(data);
            MR_index_or_search_ptag_layout(ptag, ptag_layout);

            switch (ptag_layout->MR_sectag_locn) {
                case MR_SECTAG_NONE:
                case MR_SECTAG_NONE_DIRECT_ARG:
                    sectag = 0;
                    break;
                case MR_SECTAG_LOCAL_REST_OF_WORD:
                    sectag = MR_unmkbody(data);
                    break;
                case MR_SECTAG_LOCAL_BITS:
                    sectag = MR_unmkbody(data) &
                        ((1 << ptag_layout->MR_sectag_numbits) - 1);
                    break;
                case MR_SECTAG_REMOTE_FULL_WORD:
                    sectag = MR_field(ptag, data, 0);
                    break;
                case MR_SECTAG_REMOTE_BITS:
                    sectag = MR_field(ptag, data, 0) &
                        ((1 << ptag_layout->MR_sectag_numbits) - 1);
                    break;
                case MR_SECTAG_VARIABLE:
                    MR_fatal_error("MR_named_arg_num(): unexpected variable");
                default:
                    MR_fatal_error("MR_named_arg_num(): invalid sectag_locn");
            }

            MR_index_or_search_sectag_functor(ptag_layout, sectag,
                functor_desc);

            if (functor_desc->MR_du_functor_arg_names == NULL) {
                return MR_FALSE;
            }

            for (i = 0; i < functor_desc->MR_du_functor_orig_arity; i++) {
                if (functor_desc->MR_du_functor_arg_names[i] != NULL
                && MR_streq(arg_name,
                    functor_desc->MR_du_functor_arg_names[i]))
                {
                    *arg_num_ptr = i;
                    return MR_TRUE;
                }
            }

            return MR_FALSE;

        case MR_TYPECTOR_REP_EQUIV:
            eqv_type_info = MR_create_type_info(
                MR_TYPEINFO_GET_FIXED_ARITY_ARG_VECTOR(type_info),
                MR_type_ctor_layout(type_ctor_info).MR_layout_equiv);
            return MR_named_arg_num(eqv_type_info, term_ptr, arg_name,
                arg_num_ptr);

        case MR_TYPECTOR_REP_EQUIV_GROUND:
            eqv_type_info = MR_pseudo_type_info_is_ground(
                MR_type_ctor_layout(type_ctor_info).MR_layout_equiv);
            return MR_named_arg_num(eqv_type_info, term_ptr, arg_name,
                arg_num_ptr);

        case MR_TYPECTOR_REP_NOTAG:
        case MR_TYPECTOR_REP_NOTAG_USEREQ:
        case MR_TYPECTOR_REP_NOTAG_GROUND:
        case MR_TYPECTOR_REP_NOTAG_GROUND_USEREQ:
            notag_functor_desc = MR_type_ctor_functors(type_ctor_info).
                MR_functors_notag;

            if (notag_functor_desc->MR_notag_functor_arg_name != NULL
            && MR_streq(arg_name,
                notag_functor_desc->MR_notag_functor_arg_name))
            {
                *arg_num_ptr = 0;
                return MR_TRUE;
            }

            return MR_FALSE;

        case MR_TYPECTOR_REP_ENUM:
        case MR_TYPECTOR_REP_ENUM_USEREQ:
        case MR_TYPECTOR_REP_DUMMY:
        case MR_TYPECTOR_REP_INT:
        case MR_TYPECTOR_REP_UINT:
        case MR_TYPECTOR_REP_INT8:
        case MR_TYPECTOR_REP_UINT8:
        case MR_TYPECTOR_REP_INT16:
        case MR_TYPECTOR_REP_UINT16:
        case MR_TYPECTOR_REP_INT32:
        case MR_TYPECTOR_REP_UINT32:
        case MR_TYPECTOR_REP_INT64:
        case MR_TYPECTOR_REP_UINT64:
        case MR_TYPECTOR_REP_FLOAT:
        case MR_TYPECTOR_REP_CHAR:
        case MR_TYPECTOR_REP_STRING:
        case MR_TYPECTOR_REP_BITMAP:
        case MR_TYPECTOR_REP_FUNC:
        case MR_TYPECTOR_REP_PRED:
        case MR_TYPECTOR_REP_SUBGOAL:
        case MR_TYPECTOR_REP_VOID:
        case MR_TYPECTOR_REP_C_POINTER:
        case MR_TYPECTOR_REP_STABLE_C_POINTER:
        case MR_TYPECTOR_REP_TYPEINFO:
        case MR_TYPECTOR_REP_TYPECTORINFO:
        case MR_TYPECTOR_REP_TYPEDESC:
        case MR_TYPECTOR_REP_TYPECTORDESC:
        case MR_TYPECTOR_REP_PSEUDOTYPEDESC:
        case MR_TYPECTOR_REP_TYPECLASSINFO:
        case MR_TYPECTOR_REP_BASETYPECLASSINFO:
        case MR_TYPECTOR_REP_SUCCIP:
        case MR_TYPECTOR_REP_HP:
        case MR_TYPECTOR_REP_CURFR:
        case MR_TYPECTOR_REP_MAXFR:
        case MR_TYPECTOR_REP_REDOFR:
        case MR_TYPECTOR_REP_REDOIP:
        case MR_TYPECTOR_REP_TICKET:
        case MR_TYPECTOR_REP_TRAIL_PTR:
        case MR_TYPECTOR_REP_REFERENCE:
        case MR_TYPECTOR_REP_TUPLE:
        case MR_TYPECTOR_REP_ARRAY:
        case MR_TYPECTOR_REP_FOREIGN:
        case MR_TYPECTOR_REP_STABLE_FOREIGN:
        case MR_TYPECTOR_REP_FOREIGN_ENUM:
        case MR_TYPECTOR_REP_FOREIGN_ENUM_USEREQ:
        case MR_TYPECTOR_REP_UNUSED1:
        case MR_TYPECTOR_REP_UNUSED2:
        case MR_TYPECTOR_REP_UNKNOWN:
            return MR_FALSE;
    }

    MR_fatal_error("MR_named_arg_num: unexpected fallthrough");
}

static MR_ConstString
MR_expand_type_name(MR_TypeCtorInfo tci, MR_bool wrap)
{
    MR_String   str;
    int         len;

    len = 0;
    len += strlen(tci->MR_type_ctor_module_name);
    len += 1;   // '.'
    len += strlen(tci->MR_type_ctor_name);
    len += 1;   // '/'
    len += 4;   // arity; we do not support arities above 1024
    if (wrap) {
        len += 4;   // <<>>
    }
    len += 1;   // NULL

    if (tci->MR_type_ctor_arity > 9999) {
        MR_fatal_error("MR_expand_type_name: arity > 9999");
    }

    MR_restore_transient_hp();
    MR_allocate_aligned_string_msg(str, len, MR_ALLOC_SITE_STRING);
    MR_save_transient_hp();

    sprintf(str, wrap? "<<%s.%s/%d>>" : "%s.%s/%d",
        tci->MR_type_ctor_module_name,
        tci->MR_type_ctor_name,
        (int) tci->MR_type_ctor_arity);

    return (MR_ConstString) str;
}
