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 )