XS::Pa...eyword(3pm)

πŸŽ€ NAME πŸŽ€

Okay bestie, this page is all about XS::Parse::Keyword (3pm) β€” XS functions to assist in parsing keyword syntax. That’s the whole vibe. πŸ’…


✨ DESCRIPTION ✨

So XS::Parse::Keyword gives you a bunch of XS functions that help you write syntax modules β€” you know, the kind that give Perl some shiny new perl-visible syntax. It’s mainly for authors of keyword plugins using the PL_keyword_plugin hook mechanism. If that’s not you, honestly? It’s unlikely to be of much use to anyone else. And it’s highly unlikely to be useful when you’re writing Perl code that uses these things. Basically: unless you are writing a keyword plugin using XS, this module is not for you. No cap. πŸ™ˆ

Also, heads up: this module is currently experimental, and the design is still evolving and subject to change. Later versions may break ABI compatibility, which means changes β€” or at least a rebuild β€” for any module that depends on it. So don’t get too attached to the exact shape of things, bestie. 😭


πŸ› οΈ XS FUNCTIONS πŸ› οΈ

🧚 boot_xs_parse_keyword

void boot_xs_parse_keyword(double ver);

Call this from your BOOT section to initialise the module and the parsing hooks. That’s its whole job. ✨

ver should either be 0 or a decimal number for the module version requirement; e.g.

boot_xs_parse_keyword(0.14);

🧚 register_xs_parse_keyword

void register_xs_parse_keyword(const char *keyword,
  const struct XSParseKeywordHooks *hooks, void *hookdata);

This installs a set of parsing hooks and ties them to the given keyword. After that, the keyword gets handled automatically by a keyword parser installed by XS::Parse::Keyword itself. Slay. πŸ‘‘


🌈 PARSE HOOKS 🌈

The XSParseKeywordHooks structure gives you these hook stages, and they are invoked in the order listed. Main-character energy only. πŸ’–

🚩 flags

These are the defined flags:

  • XPK_FLAG_EXPR β€” the parse or build function is expected to return KEYWORD_PLUGIN_EXPR.
  • XPK_FLAG_STMT β€” the parse or build function is expected to return KEYWORD_PLUGIN_STMT.

These two flags are mostly there to give static information at registration time, helping static parsing or other related tasks know what kind of grammatical element this keyword will produce.

  • XPK_FLAG_AUTOSEMI β€” the syntax forms a complete statement, which should be followed by a statement separator semicolon (;). That semicolon is optional at the end of a block. If it’s present, it will be consumed automatically.
  • XPK_FLAG_BLOCKSCOPE β€” the entire parse and build process gets wrapped in a pair of block_start() and block_end() calls. This ensures that, for example, any newly-introduced lexical variables do not escape from the scope of the syntax created by the keyword.
  • XPK_FLAG_PERMIT_LEXICAL β€” the keyword permits a prefixed my, so the author can indicate that it should have lexical effect. Typically this would only make sense for keywords that declare some new named entity, where normally that keyword’s entity is named in the symbol table instead.

At parse and build time, this fact is indicated by PL_parser->in_my being set to the KEY_my value, rather than its usual zero.

This flag is silently ignored on versions of Perl before 5.16, because on earlier versions the my prefix-detecting workaround causes seemingly-unrelated parse failures in utf8.pm.


πŸ’… The permit Stage

const char *permit_hintkey;
bool (*permit) (pTHX_ void *hookdata);

This is called by the installed keyword parser hook that handles keywords registered by register_xs_parse_keyword.

As a shortcut for the common case, permit_hintkey may point to a string to look up from the hints hash. If that key name isn’t found in the hints hash, then the keyword is not permitted. If the key is present, then the permit function gets invoked as normal.

If it’s not rejected by a hint key that wasn’t found in the hints hash, the function part of the stage is called next. It should inspect whether the keyword is permitted at this time β€” maybe by checking other lexical clues β€” and return true only if the keyword is permitted.

Both the string and the function are optional. Either or both may be present. If neither is present, then the keyword is always permitted β€” which is likely not what you wanted to do. 🫠


πŸ” The check Stage

void (*check)(pTHX_ void *hookdata);

Invoked once the keyword has been permitted. If present, this hook function can check the surrounding lexical context, state, or other information, and throw an exception if it is unhappy that the keyword should apply in this position. It’s giving very β€œI’m watching you” energy. πŸ‘€


πŸͺ„ The parse Stage

This stage is invoked once the keyword has been checked, and it actually parses the incoming text into an optree. It works by calling the first of the following function pointers that is not NULL. The invoked function may optionally build an optree to represent the parsed syntax, and place it into the variable addressed by out. If it doesn’t, then a simple OP_NULL will be constructed in its place.

lex_read_space() is called both before and after this stage is invoked, so in many simple cases the hook function itself doesn’t need to bother with it. Convenient, honestly. ✨

int (*parse)(pTHX_ OP **out, void *hookdata);

If present, this should consume text from the parser buffer by invoking lex_* or parse_* functions, and eventually return a KEYWORD_PLUGIN_* result value.

This is the most generic and powerful option, but it requires the most implementation work. Big main-character responsibility. πŸ’ͺ

int (*build)(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata);

If parse is not present, this is called instead after parsing a sequence of arguments, with types given by the pieces field. That field should be a zero-terminated array of piece types.

This alternative is a little less generic and powerful than providing parse yourself, but it involves much less parsing work and is shorter and easier to implement. Lowkey the sensible choice. πŸ“

int (*build1)(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata);

If neither parse nor build is present, this is called as a simpler variant of build when only a single argument is required. It takes its type from the piece1 field instead.


🧩 PIECES AND PIECE TYPES 🧩

When you use the build or build1 alternatives for the parse phase, this module parses the actual syntax automatically, according to the specification given by the pieces or piece1 field. The result of that parsing step is placed into the args or arg0 parameter to the invoked function, using a struct type with these fields:

typedef struct {
   union {
      OP *op;
      CV *cv;
      SV *sv;
      int i;
      struct {
         SV *name;
         SV *value;
      } attr;
      PADOFFSET padix;
      struct XSParseInfixInfo *infix;
   };
   int line;
} XSParseKeywordPiece;

Which field of the anonymous union is set depends on the type of the piece. The line field contains the line number of the source file where parsing of that piece began.

Some piece types are atomic, meaning their definition is self-contained. Others are structural, defined in terms of inner pieces. Together, these form an entire tree-shaped definition of the syntax that the keyword expects to find. It’s giving syntax tree realness. 🌳

Atomic types generally provide exactly one argument into the list of args β€” with the exception of literal matches, which do not provide anything. Structural types may provide an initial argument themselves, followed by a list of the values of each sub-piece they contained inside them. So while the data structure defining the syntax shape is a tree, the argument values it parses into are passed as a flat array to the build function.

Some structural types need to be able to determine whether or not syntax relating to some optional part of them is present in the incoming source text. In that case, the pieces relating to those optional parts must support probing. This ability is also noted below.

Many of the atomic piece types have a variant that is optional; if the given input does not look like the expected syntax for the piece type, then an _OPT-suffixed version of the type will instead yield NULL in its result pointer.

The type of each piece should be one of these macro values. πŸ‘‡


🧱 XPK_BLOCK

atomic, can probe, emits op.

XPK_BLOCK

A brace-delimited block of code is expected, and it’s passed as an optree in the op field. This will be parsed as a block within the current function scope.

You can probe this by checking for the presence of an open-brace ({) character.

Be careful defining grammars with this, because an open-brace is also a valid character to start a term expression, for example. Given a choice between XPK_BLOCK and XPK_TERMEXPR, either of them could try to consume such code as:
{ 123, 456 }

🌸 XPK_BLOCK_VOIDCTX, XPK_BLOCK_SCALARCTX, XPK_BLOCK_LISTCTX

These are variants of XPK_BLOCK that wrap a void, scalar, or list-context scope around the block. Cute little context blankets. πŸŽ€


🧚 XPK_PREFIXED_BLOCK

structural, emits op.

XPK_PREFIXED_BLOCK(pieces ...)

Some pieces are expected, followed by a brace-delimited block of code, which is passed as an optree in the op field. The prefix pieces are parsed first, and their results are passed before the block itself.

The entire sequence β€” including the prefix items β€” is contained within a pair of block_start() / block_end() calls. This permits the prefix pieces to introduce new items into the lexical scope of the block, for example by using XPK_LEXVAR_MY.

A call to intro_my() is automatically made at the end of the prefix pieces, before the block itself is parsed, ensuring any new lexical variables are now visible. Obsessed with that for us. πŸ’–

In addition, the following extra piece types are recognised here:

πŸ› οΈ XPK_SETUP

void setup(pTHX_ void *hookdata);
XPK_SETUP(&setup)

atomic, emits nothing.

This piece type runs a function given by pointer. Typically that function may be used to introduce new lexical state into the parser, or in some other way have a side-effect on the parsing context of the block to be parsed.


🌈 XPK_PREFIXED_BLOCK_ENTERLEAVE

A variant of XPK_PREFIXED_BLOCK that additionally wraps the entire parsing operation β€” including the block_start(), block_end(), and any calls to XPK_SETUP functions β€” within an ENTER/LEAVE pair.

This should not make a difference to the standard parser pieces provided here, but it may be useful behaviour for the code in the setup function, especially if it wishes to modify parser state and use the savestack to ensure it is restored again when parsing has finished. Very β€œclean up after yourself” of it.


πŸ¦‹ XPK_ANONSUB

atomic, emits cv.

A brace-delimited block of code is expected here, and it gets assembled into the body of a brand-new anonymous subroutine πŸ’– That whole thing then gets passed along as a protosub CV in the cv field.


πŸ¦‹ XPK_STAGED_ANONSUB

XPK_STAGED_ANONSUB(stages ...)

structural, emits cv.

So this is the deluxe version of XPK_ANONSUB β€” it takes extra function pointers that get invoked at various points while the parsing and compiling is happening. You can use them to interrupt the normal parse in a way that's kinda similar to what XS::Parse::Sublike does, though ngl it's currently a little less flexible.

The stages list can hold elements of the following types. You don't gotta include every stage, but whatever ones you do include have to be in this order ⬇️. Multiple copies of each stage are totally allowed β€” they get invoked in the order you wrote them, with parser code happening in between πŸ’«

XPK_ANONSUB_PREPARE

XPK_ANONSUB_PREPARE(&callback)

atomic, emits nothing.

Runs your callback before start_subparse().

XPK_ANONSUB_START

XPK_ANONSUB_START(&callback)

atomic, emits nothing.

Runs your callback after block_start() but before the actual block contents get parsed.

XPK_ANONSUB_END

OP *op_wrapper_callback(pTHX_ OP *o, void *hookdata);

XPK_ANONSUB_END(&op_wrapper_callback)

atomic, emits nothing.

Runs your callback after the block contents are parsed but before block_end() is called. The callback is allowed to mess with the optree if it needs to, and hand back a new one ✨

XPK_ANONSUB_WRAP

XPK_ANONSUB_WRAP(&op_wrapper_callback)

atomic, emits nothing.

Runs your callback after block_end() but before the optree gets passed to newATTRSUB(). Again, the callback may modify the optree if required and return a new one.


🌸 XPK_ARITHEXPR, XPK_ARITHEXPR_OPT

atomic, emits op.

XPK_ARITHEXPR

An arithmetic expression is expected, parsed with parse_arithexpr(), and passed as an optree in the op field.

πŸŽ€ XPK_ARITHEXPR_VOIDCTX, XPK_ARITHEXPR_OPT / XPK_ARITHEXPR_SCALARCTX, XPK_ARITHEXPR_SCALARCTX_OPT

These are variants of XPK_ARITHEXPR that put the expression into void or scalar context.


⭐ XPK_TERMEXPR, XPK_TERMEXPR_OPT

atomic, emits op.

XPK_TERMEXPR

A term expression is expected, parsed with parse_termexpr(), and passed as an optree in the op field.

πŸ’œ XPK_TERMEXPR_VOIDCTX, XPK_TERMEXPR_VOIDCTX / XPK_TERMEXPR_SCALARCTX, XPK_TERMEXPR_SCALARCTX_OPT

Variants of XPK_TERMEXPR that put the expression into void or scalar context. (Yes, XPK_TERMEXPR_VOIDCTX shows up twice in that list on the page itself β€” we don't make the rules, we just report them πŸ’€)


🧚 XPK_LISTEXPR, XPK_LISTEXPR_OPT

atomic, emits op.

XPK_LISTEXPR

A list expression is expected, parsed with parse_listexpr(), and passed as an optree in the op field.

πŸ“ XPK_LISTEXPR_LISTCTX, XPK_LISTEXPR_LISTCTX_OPT

A variant of XPK_LISTEXPR that puts the expression into list context.


πŸ‘‘ XPK_PREFIXED_TERMEXPR_ENTERLEAVE, XPK_PREFIXED_LISTEXPR_ENTERLEAVE

XPK_PREFIXED_TERMEXPR_ENTERLEAVE(pieces ...)
XPK_PREFIXED_LISTEXPR_ENTERLEAVE(pieces ...)

These are variants of XPK_TERMEXPR or XPK_LISTEXPR that expect a sequence of pieces first, before they go and parse a term expression β€” same kind of deal as how XPK_PREFIXED_BLOCK_ENTERLEAVE works. The whole operation gets wrapped up in an ENTER/LEAVE pair 🫢

They're intended strictly for use with XPK_SETUP pieces as prefixes. Any other piece that actually parses real input is likely to give you an overly-complex, subtle, or honestly just outright ambiguous grammar, and you should avoid that fr fr πŸ™ˆ

πŸ¦‹ XPK_IDENT, XPK_IDENT_OPT

atomic, can probe, emits sv.

A bareword identifier name is expected, and it comes back as an SV containing a PV in the sv field. Heads up: an identifier is not allowed to contain a double colon (::).

🌈 XPK_PACKAGENAME, XPK_PACKAGENAME_OPT

atomic, can probe, emits sv.

A bareword package name is expected, passed as an SV containing a PV in the sv field. A package name is basically an identifier's sibling, except it does permit double colons in the middle πŸ’…


πŸŽ€ XPK_LEXVARNAME

XPK_LEXVARNAME(kind)

atomic, emits sv.

A lexical variable name is expected, and it gets passed as an SV containing a PV in the sv field. The kind argument says which kinds of variable are allowed β€” it should be a bitmask with one or more bits from XPK_LEXVAR_SCALAR, XPK_LEXVAR_ARRAY and XPK_LEXVAR_HASH. There's a handy shortcut, XPK_LEXVAR_ANY, which permits all three 😌


✨ XPK_ATTRIBUTES

atomic, emits i followed by more args.

A list of :-prefixed attributes is expected, in the same format as sub or variable attributes. An optional leading : signals that attributes are present, and then one or more of them get parsed. Attributes can optionally be separated by extra :s, but that's not required.

Each attribute is expected to be an identifier name, followed by an optional value wrapped in parentheses. Whitespace is NOT permitted between the name and the value β€” standard Perl parsing rules, bestie 🚨

:attrname
:attrname(value)

The i field tells you how many attributes were found. That number of additional arguments then gets passed, each one holding two SVs in the attr.name and attr.value fields. That count is allowed to be zero!

It is not an error for there to be no attributes present, or for the optional colon to be missing. In that case i just gets set to zero πŸŽ”


πŸ’– XPK_VSTRING, XPK_VSTRING_OPT

atomic, can probe, emits sv.

A version string is expected, in the form v1.234 β€” leading v character and all. It comes back as a version SV object in the sv field.


🌸 XPK_LEXVAR

XPK_LEXVAR(kind)

atomic, emits padix.

A lexical variable name is expected, and it gets looked up from the current pad. The resulting pad index is passed in the padix field. If the variable isn't found, no error happens β€” you just get the value NOT_IN_PAD instead πŸ™ˆ

The kind argument says which kinds of variable are allowed, exactly as per XPK_LEXVARNAME.

πŸ’œ XPK_LEXVAR_MY

XPK_LEXVAR_MY(kind)

atomic, emits padix.

A lexical variable name is expected, gets added to the current pad as if it had been written in a my expression, and then comes back as the pad index in the padix field.

The kind argument says which kinds of variable are allowed, same deal as XPK_LEXVARNAME.


πŸŽ€ XPK_COMMA, XPK_COLON, XPK_EQUALS

atomic, can probe, emits nothing.

A literal character (",", ":" or "=") is expected. No argument value gets passed πŸ’«


⭐ XPK_AUTOSEMI

atomic, emits nothing.

A literal semicolon (";") as a statement terminator is optionally expected. If the next token is a closing brace marking the end of a block, then no semicolon is required. If anything else shows up, an error gets raised 🚨

This piece type does exactly the same thing as specifying XPK_FLAG_AUTOSEMI. It's super useful at the end of a sequence that forms part of a choice of syntax, where some forms end the statement with a semicolon and others end in a full block that doesn't need one πŸ‘‘


πŸ”₯ XPK_INFIX_*

atomic, can probe, emits infix.

An infix operator as recognised by XS::Parse::Infix. The pointer you get back points to a structure allocated by XS::Parse::Infix that describes the operator.

There are several versions of the macro, each using a different selection filter to pick certain available infix operators:

XPK_INFIX_RELATION         // any relational operator
XPK_INFIX_EQUALITY         // an equality operator like `==` or `eq`
XPK_INFIX_MATCH_NOSMART    // any sort of "match"-like operator, except smartmatch
XPK_INFIX_MATCH_SMART      // XPK_INFIX_MATCH_NOSMART plus smartmatch

πŸ¦‹ XPK_LITERAL

XPK_LITERAL("literal")

atomic, can probe, emits nothing.

A literal string match is expected. No argument value gets passed.

Definitely avoid this form if you possibly can, bestie 😭 it's so easy to abuse and end up with syntaxes that confuse humans and code tools alike. Generally it's best reserved for just the first component of an XPK_OPTIONAL or XPK_REPEATED sequence, to provide a "secondary keyword" that the repeated item can look out for.

🧚 XPK_KEYWORD

XPK_KEYWORD("keyword")

atomic, can probe, emits nothing.

A literal string match is expected. No argument value gets passed.

This is like XPK_LITERAL, except it also checks that the character right after it isn't an identifier character. That keeps the keyword-like behaviour actually keyword-like ✨ For example, given the input keyword, the piece XPK_LITERAL("key") would match it β€” but XPK_KEYWORD("key") would not, because of the w sitting right there after it πŸ’€


πŸ“ XPK_INTRO_MY

atomic, emits nothing.

Calls the core perl intro_my() function immediately. No input is consumed and no output value is generated. This is often handy right after XPK_LEXVAR_MY 🫢


🌈 XPK_WARNING

XPK_WARNING("message here")

atomic, emits nothing.

Emits a warning by calling the core perl warn() function on the given string literal. It's equivalent to just calling warn() from the build function, except this one fires immediately at parse time, so the line numbering will be more accurate 🎯 Also, if you place it as part of an optional or choice sequence, the warning only gets emitted conditionally β€” when that part of the grammar structure is actually encountered.

πŸ’… XPK_WARNING_...

There are several variants of XPK_WARNING that only fire when particular warning categories are enabled. These are the ones likely to be useful at parse time:

XPK_WARNING_AMBIGUOUS
XPK_WARNING_DEPRECATED
XPK_WARNING_EXPERIMENTAL
XPK_WARNING_PRECEDENCE
XPK_WARNING_SYNTAX

πŸŽ€ XPK_SEQUENCE

XPK_SEQUENCE(pieces ...)

structural, might support probe, emits nothing.

A structural type that holds a number of pieces. Normally this is just the same as laying the pieces out in sequence inside their own container, but it's genuinely useful inside XPK_CHOICE or XPK_TAGGEDCHOICE πŸ’«

An XPK_SEQUENCE supports probe if its first contained piece does β€” like, it's transparent to probing πŸ‘‘


🌸 XPK_OPTIONAL

XPK_OPTIONAL(pieces ...)

structural, emits i.

A structural type that may expect to find its contained pieces, or is perfectly happy not to 🌷 It passes an argument whose i field holds either 1 or 0, depending on whether the contents were found. The first piece type inside must support probe.


πŸ’œ XPK_REPEATED

XPK_REPEATED(pieces ...)

structural, emits i.

A structural type that expects zero or more repeats of its contained pieces. It passes an argument whose i field holds the count of how many repeats it found. The first piece type inside must support probe.


⭐ XPK_CHOICE

XPK_CHOICE(options ...)

structural, can probe, emits i.

A structural type that expects to find one of a number of alternative options. You hand it an ordered list of types, and all of them must support probe. It passes an argument whose i field gives the index of the first choice that was accepted β€” and the first option takes the value 0 🎯

…and that's the vibe of alternatives, bestie. As each of the options is interpreted as an alternative, not a sequence, you should use XPK_SEQUENCE if a sequence of multiple items should be considered as a single alternative.

Okay so real talk: it is not an error if no choice matches 😌. At that point, the i field will be set to -1.

If you require a failure message in this case, set the final choice to be of type XPK_FAILURE. This will cause an error message to be printed instead.

XPK_FAILURE("message string")

🏷️ XPK_TAGGEDCHOICE 🏷️

structural, can probe, emits i.

XPK_TAGGEDCHOICE(choice, tag, ...)

A structural type similar to XPK_CHOICE, except that each choice type is followed by an element of type XPK_TAG which gives an integer. It is that integer value, rather than the positional index of the choice within the list, which is passed in the i field. πŸ‘‘

XPK_TAG(value)

As each of the options is interpreted as an alternative, not a sequence, you should use XPK_SEQUENCE if a sequence of multiple items should be considered as a single alternative. (Yeah I know, same warning, it's important okay πŸ’…)


πŸ“ XPK_COMMALIST πŸ“

structural, might support probe, emits i.

XPK_COMMALIST(pieces ...)

A structural type which expects to find one or more repeats of its contained pieces, separated by literal comma (,",") characters. This is somewhat similar to XPK_REPEATED, except that it needs at least one copy, needs commas between its items, but does not require that the first contained piece support probe (the comma itself is sufficient to indicate a repeat). πŸŽ€

An XPK_COMMALIST supports probe if its first contained piece does; i.e. is transparent to probing.


πŸ¦‹ XPK_PARENS πŸ¦‹

structural, can probe, emits nothing.

XPK_PARENS(pieces ...)

A structural type which expects to find a sequence of pieces, all contained in parentheses as ( ... ). This will pass no extra arguments.


🌸 XPK_ARGS 🌸

structural, emits nothing.

XPK_ARGS(pieces ...)

A structural type similar to XPK_PARENS, except that the parentheses themselves are optional; much like Perl's parsing of calls to known functions. ✨

If parentheses are encountered in the input, they will be consumed by this piece and it will behave identically to XPK_PARENS. If there is no open parenthesis, this piece will behave like XPK_SEQUENCE and consume all the pieces inside it, without expecting a closing parenthesis.


πŸ’… XPK_BRACKETS πŸ’…

structural, can probe, emits nothing.

XPK_BRACKETS(pieces ...)

A structural type which expects to find a sequence of pieces, all contained in square brackets as [ ... ]. This will pass no extra arguments.


πŸŽ€ XPK_BRACES πŸŽ€

structural, can probe, emits nothing.

XPK_BRACES(pieces ...)

A structural type which expects to find a sequence of pieces, all contained in braces as { ... }. This will pass no extra arguments.

Note that this is not necessary to use with XPK_BLOCK or XPK_ANONSUB; those will already consume a set of braces. This is intended for special constrained syntax that should not just accept an arbitrary block. 🫢

🧚 XPK_CHEVRONS 🧚

structural, can probe, emits nothing.

XPK_CHEVRONS(pieces ...)

A structural type which expects to find a sequence of pieces, all contained in angle brackets as < ... >. This will pass no extra arguments.

Remember that expressions like a > b are valid term expressions, so the contents of this scope shouldn't allow arbitrary expressions or the closing bracket will be ambiguous. πŸ’€

πŸ‘‘ XPK_PARENS_OPT, XPK_BRACKETS_OPT, XPK_BRACES_OPT, XPK_CHEVRONS_OPT πŸ‘‘

structural, can probe, emits i.

XPK_PARENS_OPT(pieces ...)
XPK_BRACKETS_OPT(pieces ...)
XPK_BRACES_OPT(pieces ...)
XPK_CHEVERONS_OPT(pieces ...)

Each of the four contained structure macros above has an optional variant, whose name is suffixed by _OPT. These pass an argument whose i field is either true or false, indicating whether the scope was found, followed by the values from the scope itself. ⭐

This is a convenient shortcut to nesting the scope within a XPK_OPTIONAL macro.


🌈 XPK_..._pieces 🌈

XPK_SEQUENCE_pieces(ptr)
XPK_OPTIONAL_pieces(ptr)
...

For each of the XPK_... macros that takes a variable-length list of pieces, there is a variant whose name ends with ..._pieces, taking a single pointer argument directly. This must point at a const XSParseKeywordPieceType [] array whose final element is the zero element. πŸ“

Normally hand-written C code of a fixed grammar would be unlikely to use these forms, but they may be useful in dynamically-generated cases.


πŸ‘‘ AUTHOR πŸ‘‘

Paul Evans <leonerd@leonerd.org.uk>


And that's a wrap on the whole page, bestie β€” you officially know everything there is to know about XS::Parse::Keyword πŸ’–. Go forth and parse some keywords, you absolute icon. πŸ¦‹

This edition is straight from perl v5.42.0, dated 2026-09-30, and it's the XS::Parse::Keyword(3pm) page from the User Contributed Perl Documentation. Slay. πŸ’…βœ¨

πŸ“¦ where this page comes from