Affix

 view release on metacpan or  search on metacpan

infix/include/infix/infix.h  view on Meta::CPAN

 *     printf("Result: %d\n", result); // Output: Result: 42
 *
 *     infix_forward_destroy(trampoline);
 *     return 0;
 * }
 * ```
 */
#pragma once
/**
 * @defgroup version_info Version Information
 * @brief Macros defining the semantic version of the infix library.
 * @details The versioning scheme follows Semantic Versioning 2.0.0 (SemVer).
 * @{
 */
#define INFIX_MAJOR 0 /**< The major version number. Changes with incompatible API updates. */
#define INFIX_MINOR 1 /**< The minor version number. Changes with new, backward-compatible features. */
#define INFIX_PATCH 4 /**< The patch version number. Changes with backward-compatible bug fixes. */

#if defined(__has_c_attribute)
#define _INFIX_HAS_C_ATTRIBUTE(x) __has_c_attribute(x)
#else

infix/src/common/double_tap.h  view on Meta::CPAN


// C++ Headers must be included BEFORE extern "C"
#if defined(__cplusplus)
#include <atomic>
#endif

#ifdef __cplusplus
extern "C" {
#endif

// Portability Macros for Atomics and Thread-Local Storage
#if defined(__cplusplus)
#define TAP_ATOMIC_SIZE_T std::atomic<size_t>
#define TAP_ATOMIC_FETCH_ADD(ptr, val) std::atomic_fetch_add(ptr, (size_t)(val))
#define TAP_ATOMIC_INIT(val) (val)
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
#include <stdatomic.h>
#define TAP_ATOMIC_SIZE_T _Atomic size_t
#define TAP_ATOMIC_FETCH_ADD(ptr, val) atomic_fetch_add(ptr, val)
#define TAP_ATOMIC_INIT(val) = val
#elif defined(__GNUC__) || defined(__clang__)

infix/src/common/double_tap.h  view on Meta::CPAN

void tap_skip(size_t count, const char * reason, ...) DBLTAP_PRINTF_FORMAT(2, 3);
void tap_skip_all(const char * reason, ...) DBLTAP_PRINTF_FORMAT(1, 2);
void diag(const char * fmt, ...) DBLTAP_PRINTF_FORMAT(1, 2);
void tap_note(const char * fmt, ...) DBLTAP_PRINTF_FORMAT(1, 2);
void test_body(void);

#ifdef __cplusplus
}
#endif

// Public Test Harness Macros
/** @brief Declares the total number of tests to be run in the current scope. Must be called before any tests. */
#define plan(count) tap_plan(count)
/** @brief Concludes testing, validates the plan, and returns an exit code based on success or failure. */
#define done() tap_done()
/** @brief Immediately terminates the entire test suite with a failure message. Useful for fatal setup errors. */
#define bail_out(...) tap_bail_out(__VA_ARGS__)
/** @brief The core assertion macro. Checks a condition and prints an "ok" or "not ok" TAP line with diagnostics on
 * failure. */
#define ok(cond, ...) tap_ok(!!(cond), __FILE__, __LINE__, __func__, #cond, __VA_ARGS__)
/** @brief A convenience macro that always passes. Equivalent to `ok(true, ...)`. */

infix/src/common/infix_config.h  view on Meta::CPAN

 *
 * Its most critical function is to select the correct **Application Binary Interface (ABI)**
 * implementation to use for JIT code generation. This is achieved through a cascade
 * of preprocessor checks that can be overridden by the user for cross-compilation.
 * By the end of this file, exactly one `INFIX_ABI_*` macro must be defined, which
 * determines which `abi_*.c` file is included in the unity build.
 *
 * @internal
 */
#pragma once
// System Feature Test Macros
/**
 * @details These macros are defined to ensure that standard POSIX and other
 * necessary function declarations (like `dlopen`, `dlsym`, `snprintf`, `shm_open`)
 * are made available by system headers in a portable way across different C
 * library implementations (glibc, musl, BSD libc, etc.). Failing to define these
 * can lead to compilation failures due to implicitly declared functions on
 * stricter build environments.
 */
#if !defined(_POSIX_C_SOURCE)
#define _POSIX_C_SOURCE 200809L

infix/src/common/infix_internals.h  view on Meta::CPAN

 * @details Located in `src/jit/executor.c`, this function is called by the JIT-compiled
 * stub. It receives the marshalled arguments and dispatches the call to either
 * the type-safe callback (via a cached forward trampoline) or the generic closure handler.
 * @param[in] context The `infix_reverse_t` context for this call.
 * @param[out] return_value_ptr A pointer to the stack buffer for the return value.
 * @param[in] args_array A pointer to the `void**` array of argument pointers.
 */
INFIX_INTERNAL void infix_internal_dispatch_callback_fn_impl(infix_reverse_t * context,
                                                             void * return_value_ptr,
                                                             void ** args_array);
// Utility Macros & Inlines
/** @brief Appends a sequence of bytes (e.g., an instruction opcode) to a code buffer. */
#define EMIT_BYTES(buf, ...)                             \
    do {                                                 \
        const uint8_t bytes[] = {__VA_ARGS__};           \
        code_buffer_append((buf), bytes, sizeof(bytes)); \
    } while (0)
/**
 * @brief Aligns a value up to the next multiple of a power-of-two alignment.
 * @param value The value to align.
 * @param alignment The alignment boundary (must be a power of two).

lib/Affix.h  view on Meta::CPAN

    /// Type alias for an infix type registry. Represents a collection of named types.
    infix_registry_t * registry;
    /// // Smart enums
    HV * enum_registry;
    // Cache for coercion strings to avoid re-fetching from SV objects
    HV * coercion_cache;
} my_cxt_t;
START_MY_CXT;
// Helper macro to fetch a value from a hash if it exists, otherwise return a default.
#define hv_existsor(hv, key, _or) hv_exists(hv, key, strlen(key)) ? *hv_fetch(hv, key, strlen(key), 0) : _or
// Macros to handle passing the Perl interpreter context ('THX') explicitly,
// which is necessary for thread-safe code.
#ifdef MULTIPLICITY
#define storeTHX(var) (var) = aTHX
#define dTHXfield(var) tTHX var;
#else
#define storeTHX(var) dNOOP
#define dTHXfield(var)
#endif

// Forward-declare the primary structures.

lib/Affix.h  view on Meta::CPAN

#ifdef newXS_flags
#define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
#else
#define newXSproto_portable(name, c_impl, file, proto) \
    (PL_Sv = (SV *)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV *)PL_Sv)
#endif
#define newXS_deffile(a, b) Perl_newXS_deffile(aTHX_ a, b)
#define export_function(package, what, tag) \
    _export_function(aTHX_ get_hv(form("%s::EXPORT_TAGS", package), GV_ADD), what, tag)

// Debugging Macros
#if DEBUG > 1
#define PING warn("Ping at %s line %d", __FILE__, __LINE__);
#else
#define PING
#endif
#define DumpHex(addr, len) _DumpHex(aTHX_ addr, len, __FILE__, __LINE__)
void _DumpHex(pTHX_ const void *, size_t, const char *, int);
#define DD(scalar) _DD(aTHX_ scalar, __FILE__, __LINE__)
void _DD(pTHX_ SV *, const char *, int);

lib/Affix/Wrap.pm  view on Meta::CPAN

            return $type->affix_type       if builtin::blessed($type);
            return 'Void';
        }

        method affix {
            return $definition->affix if defined $definition;
            builtin::blessed($type) ? $type->affix : Void;
        }
    }
    class    #
        Affix::Wrap::Macro : isa(Affix::Wrap::Entity) {
        field $value : reader : param //= ();

        method affix_type {
            $value // return '';
            my $v = $value // '';
            $v =~ s/^\s+|\s+$//g;
            return '' unless length $v;
            if ( $v =~ /^-?(?:0x[\da-fA-F]+|\d+(?:\.\d+)?)$/ || $v =~ /^".*"$/ || $v =~ /^'.*'$/ ) {
                return $v;
            }

lib/Affix/Wrap.pm  view on Meta::CPAN

        method _walk( $node, $acc, $current_file ) {
            return unless ref $node eq 'HASH';
            my $kind      = $node->{kind} // 'Unknown';
            my $node_file = $self->_get_node_file($node);
            if ($node_file) {
                $current_file   = $self->_normalize($node_file);
                $last_seen_file = $current_file;
            }
            elsif ( defined $last_seen_file ) { $current_file = $last_seen_file; }
            if    ( $self->_is_valid_file($current_file) && !$node->{isImplicit} ) {
                if ( $kind eq 'MacroDefinitionRecord' ) {
                    if ( $node->{range} ) { $self->_macro( $node, $acc, $current_file ); }
                }
                elsif ( $kind eq 'TypedefDecl' ) { $self->_typedef( $node, $acc, $current_file ); }
                elsif ( $kind eq 'RecordDecl' || $kind eq 'CXXRecordDecl' ) {
                    $self->_record( $node, $acc, $current_file );
                    return;
                }
                elsif ( $kind eq 'EnumDecl' ) {
                    $self->_enum( $node, $acc, $current_file );
                    return;

lib/Affix/Wrap.pm  view on Meta::CPAN

            my $t = $self->_extract_trailing( $f, $e );
            return $d unless defined $t && length $t;
            return $t unless defined $d && length $d;
            return "$d\n$t";
        }

        method _macro( $n, $acc, $f ) {
            my ( $s, $e, $l, $el ) = $self->_meta($n);
            my $val = $self->_extract_macro_val( $n, $f );
            push @$acc,
                Affix::Wrap::Macro->new(
                name         => $n->{name},
                file         => $f,
                line         => $l,
                end_line     => $el,
                value        => $val,
                doc          => $self->_extract_doc( $f, $s ),
                start_offset => $s,
                end_offset   => $e
                );
        }

lib/Affix/Wrap.pm  view on Meta::CPAN

                    $v =~ s/\/\/.*$//;
                    $v =~ s/\/\*.*?\*\///g;
                    $v =~ s/^\s+|\s+$//g;
                    return $v;
                }
            }
            '';
        }

        method _scan_macros_fallback($acc) {
            my %seen = map { $_->name => 1 } grep { ref($_) eq 'Affix::Wrap::Macro' } @$acc;
            for my $f ( keys %$allowed_files ) {
                next unless $self->_is_valid_file($f);
                my $c = $self->_get_content($f);
                while ( $c =~ /^\s*#\s*define\s+(\w+)(?:[ \t]+(.*?))?\s*$/mg ) {
                    my $name = $1;
                    next if $seen{$name};
                    my $val  = $2 // '';
                    my $off  = $-[0];
                    my $end  = $+[0];
                    my $pre  = substr( $c, 0, $off );
                    my $line = ( $pre =~ tr/\n// ) + 1;
                    $val =~ s/\/\/.*$//;
                    $val =~ s/\/\*.*?\*\///g;
                    $val =~ s/^\s+|\s+$//g;
                    push @$acc,
                        Affix::Wrap::Macro->new(
                        name         => $name,
                        file         => $f,
                        line         => $line,
                        end_line     => $line,
                        value        => $val,
                        doc          => $self->_extract_doc( $f, $off ),
                        start_offset => $off,
                        end_offset   => $end
                        );
                }

lib/Affix/Wrap.pm  view on Meta::CPAN


        method _read($f) {
            my $abs = $self->_normalize($f);
            return $file_cache->{$abs} if exists $file_cache->{$abs};
            return $file_cache->{$abs} = Path::Tiny::path($f)->slurp_utf8;
        }

        method _scan( $f, $acc ) {
            my $c = $self->_read($f);

            # Macros
            while ( $c =~ /^\s*#\s*define\s+(\w+)(?:[ \t]+(.*?))?$/gm ) {
                my $name = $1;
                my $val  = $2 // '';
                my $s    = $-[0];
                my $e    = $+[0];
                $val =~ s/\/\/.*$//;
                $val =~ s/\/\*.*?\*\///g;
                $val =~ s/^\s+|\s+$//g;
                push @$acc,
                    Affix::Wrap::Macro->new(
                    name         => $name,
                    value        => $val,
                    file         => $f,
                    line         => $self->_ln( $c, $s ),
                    end_line     => $self->_ln( $c, $e ),
                    doc          => $self->_doc( $c, $s ),
                    start_offset => $s,
                    end_offset   => $e
                    );
            }

lib/Affix/Wrap.pm  view on Meta::CPAN

            # Pre-register User Types
            # This ensures they are available in the Affix registry before signatures are parsed,
            # and allows using them in recursive definitions or opaque handles.
            for my $name ( keys %$types ) {
                my $type     = $types->{$name};
                my $type_str = builtin::blessed($type) ? $type : "$type";
                Affix::typedef( $name, $type_str );
            }
            my @nodes = $self->parse;

            #  Macro resolution pass
            my %macros;
            for my $node (@nodes) {
                if ( $node isa Affix::Wrap::Macro ) {
                    my $val = $node->value // '';

                    # Strip C suffixes (U, L, UL, LL) from hex/decimal numbers
                    # e.g. 0x01U -> 0x01
                    $val =~ s/(?<=\d)[Uu][Ll]{0,2}//g;
                    $macros{ $node->name } = $val;
                }
            }
            my %cache;
            my $resolve;

lib/Affix/Wrap.pm  view on Meta::CPAN


            # Generation pass
            my @installed;
            for my $node (@nodes) {

                # Skip definitions if the user provided a manual type override
                if ( ( $node isa Affix::Wrap::Typedef || $node isa Affix::Wrap::Struct || $node isa Affix::Wrap::Enum ) &&
                    exists $types->{ $node->name } ) {
                    next;
                }
                if ( $node isa Affix::Wrap::Macro ) {

                    # Attempt to resolve value to a pure number
                    my $val = $resolve->( $node->name );
                    if ( defined $val ) {

                        # It's a number. Inject constant sub returning integer
                        no strict 'refs';
                        no warnings 'redefine';
                        *{ "${target}::" . $node->name } = sub () {$val};
                        push @installed, $node;

lib/Affix/Wrap.pod  view on Meta::CPAN

A global C<extern> variable.

=over

=item * C<affix_type>: Returns string C<pin my $var, $lib, name =E<gt> Type>.

=item * C<affix( $lib, $pkg )>: Installs the variable accessor into C<$pkg>.

=back

=head2 Affix::Wrap::Macro

A preprocessor C<#define>. Only simple value macros are captured.

=over

=item * C<affix_type>: Returns string C<use constant Name =E<gt> Value>. Expressions (e.g., C<A + B>) are quoted as strings, while literals are preserved.

=item * C<affix( undef, $pkg )>: Installs the constant into C<$pkg>.

=back

lib/Affix/Wrap.pod  view on Meta::CPAN


        # Generate Perl code
        if ( $node isa Affix::Wrap::Function ) {
            # e.g. "affix $lib, 'my_func', [Int], Void;"
            $code .= $node->affix_type . "\n";
        }
        elsif ( $node isa Affix::Wrap::Typedef ) {
            # e.g. "typedef MyStruct => Struct[ ... ];"
            $code .= $node->affix_type . ";\n";
        }
        elsif ( $node isa Affix::Wrap::Macro ) {
            # e.g. "use constant MAX_VAL => 100;"
            $code .= $node->affix_type . ";\n";
        }
        elsif ( $node isa Affix::Wrap::Variable ) {
            # e.g. "pin my $var, $lib, ..."
            $code .= $node->affix_type . ";\n";
        }
    }

    $code .= "\n1;\n";



( run in 0.698 second using v1.01-cache-2.11-cpan-0a27d97929d )