<commit_before>/**
 * Macros to create and manipulate metatypes.
 */

#ifndef NSTL_INTERNAL_TYPE_H
#define NSTL_INTERNAL_TYPE_H

#include <nstl/internal/config.h>
#include <nstl/internal/field.h>
#include <nstl/internal/token.h>

#include <joy/seq/append.h>
#include <joy/seq/contains.h>
#include <joy/seq/find_first.h>
#include <joy/seq/remove_if.h>
#include <joy/string/eq.h>

#include <chaos/preprocessor/recursion/expr.h>
#include <chaos/preprocessor/recursion/basic.h>
#include <chaos/preprocessor/seq/filter.h>
#include <chaos/preprocessor/seq/fold_left.h>
#include <chaos/preprocessor/seq/elem.h>
#include <chaos/preprocessor/seq/replace.h>
#include <chaos/preprocessor/seq/for_each.h>
#include <chaos/preprocessor/logical/nor.h>
#include <chaos/preprocessor/logical/and.h>
#include <chaos/preprocessor/control/if.h>
#include <chaos/preprocessor/control/when.h>
#include <chaos/preprocessor/control/unless.h>
#include <chaos/preprocessor/cat.h>


/****************************************************************************
                                 NSTL_TYPE
 ****************************************************************************/

/**
 * Create a new nstl type.
 *
 * @param unique_id A unique C identifier representing the type.
 *                  For example, a nstl type defining a template function
 *                  could have a unique id composed from the base name of
 *                  the function and the unique id of the type used to
 *                  parametrize the template.
 *
 * @param instructions A sequence of instructions:
 *                     (instruction_1 args...)
 *                     (instruction_2 args...)
 *                              ...
 *                     (instruction_n args...)
 */
#define NSTL_TYPE(unique_id, instructions) \
    NSTL_TYPE_S(CHAOS_PP_STATE(), unique_id, instructions)

#define NSTL_TYPE_S(s, unique_id, instructions)                                \
    CHAOS_PP_EXPR_S(s)(CHAOS_PP_SEQ_FOLD_LEFT_S(s,                             \
        NSTL_I_EXECUTE_STATEMENT,                                              \
        instructions,                                                          \
        (unique_id) (NSTL_I_INITIAL_FIELDS_STATE())                            \
    ))                                                                         \
/**/

/* This is necessary because we can not have an empty argument as the initial
 * state in C89.
 */
#if NSTL_CONFIG_EMPTY_MACRO_ARGS
#   define NSTL_I_INITIAL_FIELDS_STATE() /* nothing */
#else
#   define NSTL_I_INITIAL_FIELDS_STATE() (NSTL_FIELD(~, anonymous, ~))
#endif /* NSTL_CONFIG_EMPTY_MACRO_ARGS */

/**
 * Execute a statement of the form ``instruction args...''.
 */
#define NSTL_I_EXECUTE_STATEMENT(s, stmnt, self)                               \
    NSTL_II_EXECUTE_STATEMENT(s, self,                                         \
        NSTL_TOKEN_STRING_HEAD(stmnt),                                         \
        /* we use the UNSAFE version here because we have no clue what can the \
         * arguments be, so we can't make assumptions about them being a valid \
         * token string.                                                       \
         */                                                                    \
        NSTL_TOKEN_STRING_TAIL_UNSAFE(stmnt)                                   \
    )                                                                          \
/**/

#define NSTL_II_EXECUTE_STATEMENT(s, self, instruction, args) \
    NSTL_INSTRUCTION(instruction)(s, self, args)

/**
 * Return whether two fields have the same name.
 */
#define NSTL_I_TYPE_COMPARE(s, x, y)                                           \
    CHAOS_PP_AND                                                               \
        (CHAOS_PP_NOR                                                          \
            (NSTL_FIELD_IS_ANONYMOUS(x))                                       \
            (NSTL_FIELD_IS_ANONYMOUS(y)))                                      \
        (JOY_STRING_EQ_S(s,                                                    \
            NSTL_TOKEN_TO_STRING(NSTL_FIELD_NAME(x)),                          \
            NSTL_TOKEN_TO_STRING(NSTL_FIELD_NAME(y))))                         \
/**/

/**
 * Given a token, return the nstl style instruction associated to it.
 *
 * @note In order for this macro to work properly, the NSTL_INSTRUCTION_instr
 *       macro must be defined properly.
 */
#define NSTL_INSTRUCTION(instr) CHAOS_PP_CAT(NSTL_INSTRUCTION_, instr)

/**
 * Return the unique identifier of a nstl type.
 */
#define NSTL_IDENTIFIER(self) CHAOS_PP_SEQ_ELEM(0, self)

/**
 * Return the fields of a nstl type.
 */
#define NSTL_I_TYPE_FIELDS(self) CHAOS_PP_SEQ_ELEM(1, self)

/**
 * Replace the fields of a nstl type.
 */
#define NSTL_I_TYPE_SET_FIELDS(self, fields) \
    CHAOS_PP_SEQ_REPLACE(1, self, fields)

/****************************************************************************
                                  NSTL_GETF
 ****************************************************************************/

/**
 * Return the value of a field of an object.
 *
 * @param field A valid nstl token.
 */
#define NSTL_GETF(self, field) \
    NSTL_GETF_S(CHAOS_PP_STATE(), self, field)

#define NSTL_GETF_S(s, self, field)                                            \
    CHAOS_PP_WHEN(NSTL_ISSET_S(s, self, field))(                               \
        NSTL_FIELD_VALUE(NSTL_I_GETF(s, self, field))                          \
    )                                                                          \
/**/

/**
 * Return a field of an object. If the field is not set, it is undefined
 * behavior.
 *
 * @note This is an internal macro returning the whole field of the object.
 *       @em NSTL_GETF must be used instead.
 */
#define NSTL_I_GETF(s, self, field)                                            \
    JOY_SEQ_FIND_FIRST_S(s,                                                    \
        NSTL_I_TYPE_COMPARE, NSTL_I_TYPE_FIELDS(self),                         \
                                NSTL_FIELD_S(s, field, __nstl_dummy_field, ~)  \
    )                                                                          \
/**/

/****************************************************************************
                                  NSTL_SETF
 ****************************************************************************/

/**
 * Set or override a field of an object.
 *
 * @param field A valid nstl token.
 * @param properties A token string of the properties of the field.
 * @param value Anything containing no commas if < C99, and anything
 *              if >= C99. This will be the value of the field that can be
 *              retrieved by using @em NSTL_GETF().
 */
#define NSTL_SETF(self, field, properties, value) \
    NSTL_SETF_S(CHAOS_PP_STATE(), self, field, properties, value)

#define NSTL_SETF_S(s, self, field, properties, value)                         \
    NSTL_I_TYPE_SET_FIELDS(self,                                               \
        JOY_SEQ_APPEND(                                                        \
            NSTL_FIELD_S(s, field, properties, value),                         \
            NSTL_I_TYPE_FIELDS(NSTL_UNSETF_S(s, self, field))                  \
        )                                                                      \
    )                                                                          \
/**/

/**
 * Instruction counterpart of @em NSTL_SETF().
 *
 * Usage: @code (setf field_name (field_properties...) field_value) @endcode
 *
 * Where @em field_name is any valid nstl token, @em field_properties is a
 * token string of properties, and @em field_value is anything
 * (without commas if < C99).
 */
#define NSTL_INSTRUCTION_setf(s, self, field_properties_value)                 \
    NSTL_I_INSTRUCTION_setf(s, self,                                           \
        NSTL_TOKEN_STRING_HEAD(field_properties_value),                        \
        /* We use the UNSAFE version here because the properties are inside    \
         * parenthesis and it is not a valid token string.                     \
         */                                                                    \
        NSTL_TOKEN_STRING_TAIL_UNSAFE(field_properties_value)                  \
    )                                                                          \
/**/<commit_msg>Fix bug where we tried to remove an anonymous field.<commit_after>/**
 * Macros to create and manipulate metatypes.
 */

#ifndef NSTL_INTERNAL_TYPE_H
#define NSTL_INTERNAL_TYPE_H

#include <nstl/internal/config.h>
#include <nstl/internal/field.h>
#include <nstl/internal/token.h>

#include <joy/seq/append.h>
#include <joy/seq/contains.h>
#include <joy/seq/find_first.h>
#include <joy/seq/remove_if.h>
#include <joy/string/eq.h>

#include <chaos/preprocessor/cat.h>
#include <chaos/preprocessor/control/if.h>
#include <chaos/preprocessor/control/iif.h>
#include <chaos/preprocessor/control/unless.h>
#include <chaos/preprocessor/control/when.h>
#include <chaos/preprocessor/logical/and.h>
#include <chaos/preprocessor/logical/nor.h>
#include <chaos/preprocessor/recursion/basic.h>
#include <chaos/preprocessor/recursion/expr.h>
#include <chaos/preprocessor/seq/elem.h>
#include <chaos/preprocessor/seq/filter.h>
#include <chaos/preprocessor/seq/fold_left.h>
#include <chaos/preprocessor/seq/for_each.h>
#include <chaos/preprocessor/seq/replace.h>


/****************************************************************************
                                 NSTL_TYPE
 ****************************************************************************/

/**
 * Create a new nstl type.
 *
 * @param unique_id A unique C identifier representing the type.
 *                  For example, a nstl type defining a template function
 *                  could have a unique id composed from the base name of
 *                  the function and the unique id of the type used to
 *                  parametrize the template.
 *
 * @param instructions A sequence of instructions:
 *                     (instruction_1 args...)
 *                     (instruction_2 args...)
 *                              ...
 *                     (instruction_n args...)
 */
#define NSTL_TYPE(unique_id, instructions) \
    NSTL_TYPE_S(CHAOS_PP_STATE(), unique_id, instructions)

#define NSTL_TYPE_S(s, unique_id, instructions)                                \
    CHAOS_PP_EXPR_S(s)(CHAOS_PP_SEQ_FOLD_LEFT_S(s,                             \
        NSTL_I_EXECUTE_STATEMENT,                                              \
        instructions,                                                          \
        (unique_id) (NSTL_I_INITIAL_FIELDS_STATE())                            \
    ))                                                                         \
/**/

/* This is necessary because we can not have an empty argument as the initial
 * state in C89.
 */
#if NSTL_CONFIG_EMPTY_MACRO_ARGS
#   define NSTL_I_INITIAL_FIELDS_STATE() /* nothing */
#else
#   define NSTL_I_INITIAL_FIELDS_STATE() (NSTL_FIELD(~, anonymous, ~))
#endif /* NSTL_CONFIG_EMPTY_MACRO_ARGS */

/**
 * Execute a statement of the form ``instruction args...''.
 */
#define NSTL_I_EXECUTE_STATEMENT(s, stmnt, self)                               \
    NSTL_II_EXECUTE_STATEMENT(s, self,                                         \
        NSTL_TOKEN_STRING_HEAD(stmnt),                                         \
        /* we use the UNSAFE version here because we have no clue what can the \
         * arguments be, so we can't make assumptions about them being a valid \
         * token string.                                                       \
         */                                                                    \
        NSTL_TOKEN_STRING_TAIL_UNSAFE(stmnt)                                   \
    )                                                                          \
/**/

#define NSTL_II_EXECUTE_STATEMENT(s, self, instruction, args) \
    NSTL_INSTRUCTION(instruction)(s, self, args)

/**
 * Return whether two fields have the same name.
 */
#define NSTL_I_TYPE_COMPARE(s, x, y)                                           \
    CHAOS_PP_AND                                                               \
        (CHAOS_PP_NOR                                                          \
            (NSTL_FIELD_IS_ANONYMOUS(x))                                       \
            (NSTL_FIELD_IS_ANONYMOUS(y)))                                      \
        (JOY_STRING_EQ_S(s,                                                    \
            NSTL_TOKEN_TO_STRING(NSTL_FIELD_NAME(x)),                          \
            NSTL_TOKEN_TO_STRING(NSTL_FIELD_NAME(y))))                         \
/**/

/**
 * Given a token, return the nstl style instruction associated to it.
 *
 * @note In order for this macro to work properly, the NSTL_INSTRUCTION_instr
 *       macro must be defined properly.
 */
#define NSTL_INSTRUCTION(instr) CHAOS_PP_CAT(NSTL_INSTRUCTION_, instr)

/**
 * Return the unique identifier of a nstl type.
 */
#define NSTL_IDENTIFIER(self) CHAOS_PP_SEQ_ELEM(0, self)

/**
 * Return the fields of a nstl type.
 */
#define NSTL_I_TYPE_FIELDS(self) CHAOS_PP_SEQ_ELEM(1, self)

/**
 * Replace the fields of a nstl type.
 */
#define NSTL_I_TYPE_SET_FIELDS(self, fields) \
    CHAOS_PP_SEQ_REPLACE(1, self, fields)

/****************************************************************************
                                  NSTL_GETF
 ****************************************************************************/

/**
 * Return the value of a field of an object.
 *
 * @param field A valid nstl token.
 */
#define NSTL_GETF(self, field) \
    NSTL_GETF_S(CHAOS_PP_STATE(), self, field)

#define NSTL_GETF_S(s, self, field)                                            \
    CHAOS_PP_WHEN(NSTL_ISSET_S(s, self, field))(                               \
        NSTL_FIELD_VALUE(NSTL_I_GETF(s, self, field))                          \
    )                                                                          \
/**/

/**
 * Return a field of an object. If the field is not set, it is undefined
 * behavior.
 *
 * @note This is an internal macro returning the whole field of the object.
 *       @em NSTL_GETF must be used instead.
 */
#define NSTL_I_GETF(s, self, field)                                            \
    JOY_SEQ_FIND_FIRST_S(s,                                                    \
        NSTL_I_TYPE_COMPARE, NSTL_I_TYPE_FIELDS(self),                         \
                                NSTL_FIELD_S(s, field, __nstl_dummy_field, ~)  \
    )                                                                          \
/**/

/****************************************************************************
                                  NSTL_SETF
 ****************************************************************************/

/**
 * Set or override a field of an object.
 *
 * @param field A valid nstl token.
 * @param properties A token string of the properties of the field.
 * @param value Anything containing no commas if < C99, and anything
 *              if >= C99. This will be the value of the field that can be
 *              retrieved by using @em NSTL_GETF().
 */
#define NSTL_SETF(self, field, properties, value) \
    NSTL_SETF_S(CHAOS_PP_STATE(), self, field, properties, value)

#define NSTL_SETF_S(s, self, field, properties, value)                         \
    NSTL_I_TYPE_SET_FIELDS(self,                                               \
        JOY_SEQ_APPEND(                                                        \
            NSTL_FIELD_S(s, field, properties, value),                         \
            NSTL_I_TYPE_FIELDS(                                                \
                CHAOS_PP_IIF(NSTL_FIELD_IS_ANONYMOUS(                          \
                        NSTL_FIELD_S(s, field, properties, value))) (          \
                    self,                                                      \
                    NSTL_UNSETF_S(s, self, field)                              \
                )                                                              \
            )                                                                  \
        )                                                                      \
    )                                                                          \
/**/

/**
 * Instruction counterpart of @em NSTL_SETF().
 *
 * Usage: @code (setf field_name (field_properties...) field_value) @endcode
 *
 * Where @em field_name is any valid nstl token, @em field_properties is a
 * token string of properties, and @em field_value is anything
 * (without commas if < C99).
 */
#define NSTL_INSTRUCTION_setf(s, self, field_properties_value)                 \
    NSTL_I_INSTRUCTION_setf(s, self,                                           \
        NSTL_TOKEN_STRING_HEAD(field_properties_value),                        \
        /* We use the UNSAFE version here because the properties are inside    \
         * parenthesis and it is not a valid token string.                     \
         */                                                                    \
        NSTL_TOKEN_STRING_TAIL_UNSAFE(field_properties_value)                  \
    )                                                                          \
/**/
{'fraction_non_alphanumeric': 0.13145110590386924, 'fraction_numerical': 0.0014859690232611304, 'mean_word_length': 1.3604478618642926, 'pattern_counts': {'":': 0, '<': 50, '<?xml version=': 0, '>': 48, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 130, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5497498', 'n_tokens_mistral': 4733, 'n_tokens_neox': 4570, 'n_words': 1052}