/*  You may distribute under the terms of either the GNU General Public License
 *  or the Artistic License (the same terms as Perl itself)
 *
 *  (C) Paul Evans, 2021 -- leonerd@leonerd.org.uk
 */

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "XSParseKeyword.h"

static const char hintkey[] = "t::structures/permit";

static int build_op(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
{
  *out = args[0]->op;
  return KEYWORD_PLUGIN_EXPR;
}

static int build_constiv(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
{
  /* npieces should always be 1 because XPK_LITERAL() does not yield args */
  *out = newSVOP(OP_CONST, 0, newSViv(args[0]->i));
  return KEYWORD_PLUGIN_EXPR;
}

static int build_constsv(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
{
  *out = newSVOP(OP_CONST, 0, args[0]->sv);
  return KEYWORD_PLUGIN_EXPR;
}

static const struct XSParseKeywordHooks hooks_sequence = {
  .permit_hintkey = hintkey,

  .pieces = (const struct XSParseKeywordPieceType []){
    XPK_SEQUENCE(
      XPK_LITERAL("part"),
      XPK_TERMEXPR
    ),
    {0}
  },
  .build = &build_op,
};

static const struct XSParseKeywordHooks hooks_optional = {
  .permit_hintkey = hintkey,

  .pieces = (const struct XSParseKeywordPieceType []){
    XPK_OPTIONAL(
      XPK_LITERAL("part")
    ),
    {0}
  },
  .build = &build_constiv,
};

static const struct XSParseKeywordHooks hooks_repeated = {
  .permit_hintkey = hintkey,

  .pieces = (const struct XSParseKeywordPieceType []){
    XPK_REPEATED(
      XPK_LITERAL("part")
    ),
    {0}
  },
  .build = &build_constiv,
};

static const struct XSParseKeywordHooks hooks_choice = {
  .permit_hintkey = hintkey,

  .pieces = (const struct XSParseKeywordPieceType []) {
    XPK_CHOICE(
      XPK_LITERAL("zero"),
      XPK_LITERAL("one"),
      XPK_LITERAL("two"),
      XPK_BLOCK
    ),
    {0}
  },
  .build = &build_constiv,
};

static const struct XSParseKeywordHooks hooks_tagged = {
  .permit_hintkey = hintkey,

  .pieces = (const struct XSParseKeywordPieceType []){
    XPK_TAGGEDCHOICE(
      XPK_LITERAL("one"),   XPK_TAG(1),
      XPK_LITERAL("two"),   XPK_TAG(2),
      XPK_LITERAL("three"), XPK_TAG(3)
    ),
    {0}
  },
  .build = &build_constiv,
};

static const struct XSParseKeywordHooks hooks_commalist = {
  .permit_hintkey = hintkey,

  .pieces = (const struct XSParseKeywordPieceType []){
    XPK_COMMALIST( XPK_LITERAL("item") ),
    {0}
  },
  .build = &build_constiv,
};

static const struct XSParseKeywordHooks hooks_parens = {
  .permit_hintkey = hintkey,

  .pieces = (const struct XSParseKeywordPieceType []){
    XPK_PARENS( XPK_TERMEXPR ),
    {0}
  },
  .build = &build_op,
};

static const struct XSParseKeywordHooks hooks_args = {
  .permit_hintkey = hintkey,

  .pieces = (const struct XSParseKeywordPieceType []){
    XPK_ARGS( XPK_TERMEXPR ),
    {0}
  },
  .build = &build_op,
};

static const struct XSParseKeywordHooks hooks_brackets = {
  .permit_hintkey = hintkey,

  .pieces = (const struct XSParseKeywordPieceType []){
    XPK_BRACKETS( XPK_TERMEXPR ),
    {0}
  },
  .build = &build_op,
};

static const struct XSParseKeywordHooks hooks_braces = {
  .permit_hintkey = hintkey,

  .pieces = (const struct XSParseKeywordPieceType []){
    XPK_BRACES( XPK_TERMEXPR ),
    {0}
  },
  .build = &build_op,
};

static const struct XSParseKeywordHooks hooks_chevrons = {
  .permit_hintkey = hintkey,

  .pieces = (const struct XSParseKeywordPieceType []){
    /* A TERMEXPR inside chevrons is ambiguous, because of the < 2 > 1 > problem */
    XPK_CHEVRONS( XPK_IDENT ),
    {0}
  },
  .build = &build_constsv,
};

MODULE = t::structures  PACKAGE = t::structures

BOOT:
  boot_xs_parse_keyword(0);

  register_xs_parse_keyword("structsequence", &hooks_sequence, NULL);
  register_xs_parse_keyword("structoptional", &hooks_optional, NULL);
  register_xs_parse_keyword("structrepeat", &hooks_repeated, NULL);
  register_xs_parse_keyword("structchoice", &hooks_choice, NULL);
  register_xs_parse_keyword("structtagged", &hooks_tagged, NULL);
  register_xs_parse_keyword("structcommalist", &hooks_commalist, NULL);

  register_xs_parse_keyword("parens",   &hooks_parens,   NULL);
  register_xs_parse_keyword("args",     &hooks_args,     NULL);
  register_xs_parse_keyword("brackets", &hooks_brackets, NULL);
  register_xs_parse_keyword("braces",   &hooks_braces,   NULL);
  register_xs_parse_keyword("chevrons", &hooks_chevrons, NULL);