Object-PadX-Enum

 view release on metacpan or  search on metacpan

lib/Object/PadX/Enum.xs  view on Meta::CPAN

      return FALSE;
   lex_read_unichar(0);
   return TRUE;
}
#define lex_consume_unichar(c) S_lex_consume_unichar(aTHX_ (c))

/* Single-keyword compile-time state. We do not support nested enums; a saved
 * snapshot at entry makes accidental nesting visible as a parse-time error
 * via `item`'s check hook rather than as silent corruption.
 */
static int  inside_enum_depth     = 0;
static SV  *current_enum_classname = NULL;

/* Construct an OP tree that, at runtime, calls a named fully-qualified Perl
 * sub with the given pre-built list of argument OPs.
 *
 * `args_list` must be an OP_LIST op (newLISTOP(OP_LIST, ...)).  Ownership of
 * `args_list` is transferred to the returned op.
 */
static OP *S_make_call_op(pTHX_ const char *subname, OP *args_list)
{
   GV *gv = gv_fetchpv(subname, GV_ADD, SVt_PVCV);
   OP *cv_ref = newCVREF(0, newGVOP(OP_GV, 0, gv));

   args_list = op_append_elem(OP_LIST, args_list, cv_ref);
   return newUNOP(OP_ENTERSUB, OPf_STACKED, args_list);
}
#define make_call_op(name, list)  S_make_call_op(aTHX_ (name), (list))

/* --------------------------------------------------------------------- */
/* `enum NAME { BODY }`                                                  */
/* --------------------------------------------------------------------- */

static int build_enum(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
{
   PERL_UNUSED_ARG(hookdata);
   PERL_UNUSED_ARG(nargs);

   SV *packagename = args[0]->sv;
   int nattrs      = args[1]->i;

   /* Snapshot prior context so a stray nested `enum` won't corrupt state. */
   SV  *saved_classname = current_enum_classname;
   int  saved_depth     = inside_enum_depth;

   current_enum_classname = packagename;
   inside_enum_depth      = 1;

   /* Marshal `[name, value_or_undef]` pairs into a Perl AV ref for the helper. */
   AV *attrs_av = newAV();
   for (int i = 0; i < nattrs; i++) {
      AV *pair = newAV();
      av_push(pair, SvREFCNT_inc(args[2 + i]->attr.name));
      SV *value = args[2 + i]->attr.value;
      av_push(pair, value ? SvREFCNT_inc(value) : newSV(0));
      av_push(attrs_av, newRV_noinc((SV *)pair));
   }
   SV *attrs_ref = sv_2mortal(newRV_noinc((SV *)attrs_av));

   /* Drive Object::Pad::MOP::Class->begin_class via the Perl helper. This
    * sets compclassmeta, registers UNITCHECK auto-seal, and adds $ordinal.
    */
   {
      dSP;
      ENTER;
      SAVETMPS;
      PUSHMARK(SP);
      XPUSHs(packagename);
      XPUSHs(attrs_ref);
      PUTBACK;
      call_pv("Object::PadX::Enum::_begin_enum", G_VOID | G_DISCARD);
      FREETMPS;
      LEAVE;
   }

   lex_read_space(0);
   if (!lex_consume_unichar('{'))
      croak("Expected '{' after 'enum %" SVf "'", SVfARG(packagename));

   ENTER;

   /* Object::Pad's `field`/`method` keywords assert PL_curstname matches
    * compclassmeta's name. begin_class sets compclassmeta but not the
    * package; do that here, mirroring Pad.xs:546-555. SAVE machinery
    * ensures restoration at the matching LEAVE.
    */
   SAVEGENERICSV(PL_curstash);
   save_item(PL_curstname);
   PL_curstash = (HV *)SvREFCNT_inc(gv_stashsv(packagename, GV_ADD));
   sv_setsv(PL_curstname, packagename);

   I32 save_ix = block_start(TRUE);

   OP *body = parse_stmtseq(0);
   body = block_end(save_ix, body);

   if (!lex_consume_unichar('}'))
      croak("Expected '}' at end of 'enum %" SVf "' body", SVfARG(packagename));

   LEAVE;

   inside_enum_depth      = saved_depth;
   current_enum_classname = saved_classname;

   /* Trailing runtime call: Object::PadX::Enum::_finalize_enum("NAME"); */
   OP *finalize_args = newLISTOP(OP_LIST, 0, NULL, NULL);
   finalize_args = op_append_elem(OP_LIST, finalize_args,
      newSVOP(OP_CONST, 0, SvREFCNT_inc(packagename)));

   OP *finalize_call = make_call_op("Object::PadX::Enum::_finalize_enum", finalize_args);
   OP *finalize_stmt = newSTATEOP(0, NULL, finalize_call);

   /* body may be NULL for an empty enum block. */
   OP *combined = body
      ? op_append_elem(OP_LINESEQ, body, finalize_stmt)
      : finalize_stmt;

   /* Wrap in a once-loop so it behaves as a single statement, mirroring
    * Object::Pad's class-block emission (Pad.xs:589-591).
    */
   *out = op_append_elem(OP_LINESEQ,



( run in 0.733 second using v1.01-cache-2.11-cpan-9581c071862 )