Eshu
view release on metacpan or search on metacpan
include/eshu_xs.h view on Meta::CPAN
* Dual-mode: delegates to eshu_c.h for C code above `MODULE =`,
* then uses XS-specific rules for XSUB declarations below.
*/
#ifndef ESHU_XS_H
#define ESHU_XS_H
#include "eshu.h"
#include "eshu_c.h"
/* ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
* XS mode tracking
* ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */
enum eshu_xs_mode {
ESHU_XS_C_MODE, /* before MODULE = line */
ESHU_XS_XSUB_MODE /* after MODULE = line */
};
enum eshu_xs_section {
ESHU_XS_NONE, /* between XSUBs / return type line */
ESHU_XS_PARAMS, /* parameter declarations */
ESHU_XS_LABEL, /* just saw a label (CODE: etc) */
ESHU_XS_BODY /* inside a label body */
};
typedef struct {
enum eshu_xs_mode mode;
enum eshu_xs_section section;
eshu_ctx_t c_ctx; /* C scanner context */
int c_depth; /* brace depth in CODE: */
int is_boot; /* inside BOOT section */
eshu_config_t cfg;
} eshu_xs_ctx_t;
static void eshu_xs_ctx_init(eshu_xs_ctx_t *ctx, const eshu_config_t *cfg) {
ctx->mode = ESHU_XS_C_MODE;
ctx->section = ESHU_XS_NONE;
ctx->c_depth = 0;
ctx->is_boot = 0;
ctx->cfg = *cfg;
eshu_ctx_init(&ctx->c_ctx, cfg);
}
/* ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
* Detection helpers
* ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */
/* Check if the line starts with MODULE followed by optional
* whitespace and '=' */
static int eshu_xs_is_module_line(const char *content, const char *eol) {
const char *p = content;
if (eol - p < 8) return 0;
if (memcmp(p, "MODULE", 6) != 0) return 0;
p += 6;
while (p < eol && (*p == ' ' || *p == '\t')) p++;
return (p < eol && *p == '=');
}
/* XS labels: CODE, INIT, OUTPUT, PREINIT, CLEANUP, POSTCALL,
* PPCODE, BOOT, CASE, INTERFACE, INTERFACE_MACRO, PROTOTYPES,
* VERSIONCHECK, INCLUDE, FALLBACK, OVERLOAD, ALIAS, ATTRS */
static int eshu_xs_is_label(const char *content, const char *eol,
int *is_boot) {
const char *p = content;
const char *start;
int len;
*is_boot = 0;
/* must start with alpha */
if (!isalpha((unsigned char)*p)) return 0;
start = p;
while (p < eol && (isalpha((unsigned char)*p) || *p == '_'))
p++;
len = (int)(p - start);
/* must be followed by ':' (possibly with whitespace) */
while (p < eol && (*p == ' ' || *p == '\t')) p++;
if (p >= eol || *p != ':') return 0;
/* check it's not a C label like 'default:' or 'case:' */
/* it also must not be a :: (package separator) */
if (p + 1 < eol && *(p + 1) == ':') return 0;
/* Known XS labels */
if ((len == 4 && memcmp(start, "CODE", 4) == 0) ||
(len == 4 && memcmp(start, "INIT", 4) == 0) ||
(len == 6 && memcmp(start, "OUTPUT", 6) == 0) ||
(len == 7 && memcmp(start, "PREINIT", 7) == 0) ||
(len == 7 && memcmp(start, "CLEANUP", 7) == 0) ||
(len == 8 && memcmp(start, "POSTCALL", 8) == 0) ||
(len == 6 && memcmp(start, "PPCODE", 6) == 0) ||
(len == 4 && memcmp(start, "CASE", 4) == 0) ||
(len == 9 && memcmp(start, "INTERFACE", 9) == 0) ||
(len == 15 && memcmp(start, "INTERFACE_MACRO", 15) == 0) ||
(len == 10 && memcmp(start, "PROTOTYPES", 10) == 0) ||
(len == 12 && memcmp(start, "VERSIONCHECK", 12) == 0) ||
(len == 7 && memcmp(start, "INCLUDE", 7) == 0) ||
(len == 8 && memcmp(start, "FALLBACK", 8) == 0) ||
(len == 8 && memcmp(start, "OVERLOAD", 8) == 0) ||
(len == 5 && memcmp(start, "ALIAS", 5) == 0) ||
(len == 5 && memcmp(start, "ATTRS", 5) == 0)) {
/* not a label that belongs to these */
} else {
return 0;
}
if (len == 4 && memcmp(start, "BOOT", 4) == 0)
*is_boot = 1;
/* BOOT is checked above but it's not in the list â add it */
return 1;
}
/* Check for BOOT: label specifically */
static int eshu_xs_is_boot_label(const char *content, const char *eol) {
const char *p = content;
if (eol - p < 5) return 0;
if (memcmp(p, "BOOT", 4) != 0) return 0;
p += 4;
while (p < eol && (*p == ' ' || *p == '\t')) p++;
return (p < eol && *p == ':' && (p + 1 >= eol || *(p + 1) != ':'));
}
/* Detect if line is a new XSUB return type / function header.
* In XS mode at depth 0, a line that starts with an identifier
* (C type name) at column 0 and is NOT a label indicates
* a new XSUB. */
static int eshu_xs_is_xsub_start(const char *content, const char *eol) {
const char *p = content;
/* Must start with alpha or underscore (type name) */
if (!isalpha((unsigned char)*p) && *p != '_')
return 0;
/* Skip the identifier */
while (p < eol && (isalnum((unsigned char)*p) || *p == '_' || *p == '*' || *p == ' ' || *p == '\t'))
p++;
/* Could be a function name with parens, or just a type */
/* This is sufficient â we rely on being in XS mode at depth 0 */
return 1;
}
/* ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
* Scan a line for C nesting changes (simplified for XS body)
* ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */
static void eshu_xs_scan_c_nesting(eshu_xs_ctx_t *ctx,
const char *p, const char *end) {
/* Track {}/()/ nesting within XSUB body code using the C scanner's
* state machine for strings/comments, but tracking depth locally */
( run in 1.021 second using v1.01-cache-2.11-cpan-5511b514fd6 )