#!perl -w
use strict;

sub code {
    my($code) = @_;
    $code =~ s/^\#//xmsg;
    return $code;
}

sub say {
    print @_, "\n";
}
binmode STDOUT;

my @ops;

print code <<"HEAD";
#/* This file is automatically generated by $0.
# * ANY CHANGES WILL BE LOST!
# */
#
HEAD

say "/* forward decl for Xslate opcodes */";
while(<>) {
    if(/^TXC(\w*) \s* \( (\w+) \)/xms) {
        push @ops, [$2, $1];

        s/\s*\{/;/;
        print;
    }
}

print code <<'H';
#
#enum tx_opcode_t {
H

for(my $i = 0; $i < @ops; $i++) {
    say "    TXOP_$ops[$i][0], /* $i */";
}

print code <<'H';
#    TXOP_last
#}; /* enum tx_opcode_t */
H

print code <<'H';
#
#static const U8 tx_oparg[] = {
H

for(my $i = 0; $i < @ops; $i++) {
    my $arg_type = $ops[$i][1];
    my $flags;
    if($arg_type) {
        $flags .= "TXCODE" . uc $arg_type;
    }
    else {
        $flags = '0U';
    }

    say "    $flags, /* $ops[$i][0] */";
}

print code <<'H';
#}; /* tx_oparg[] */
#
#static void
#tx_init_ops(pTHX_ HV* const ops) {
H

for(my $i = 0; $i < @ops; $i++) {
    say "    (void)hv_stores(ops, STRINGIFY($ops[$i][0]), newSViv(TXOP_$ops[$i][0]));";
}

print code <<'H';
#} /* tx_register_ops() */
H

print code <<'H';
#
##ifndef TX_DIRECT_THREADED_CODE
##define dTX_optable dNOOP
#static const tx_exec_t tx_optable[] = {
H

for(my $i = 0; $i < @ops; $i++) {
    say "    TXCODE_$ops[$i][0],";
}

print code <<'H';
#    NULL
#}; /* tx_optable[] */
H

print code <<'H';
#
##else /* TX_DIRECT_THREADED_CODE */
##define dTX_optable void const* const* const tx_optable \
#                    = tx_runops(aTHX_ NULL)
##define LABEL(x)     CAT2(TX_DTC_, x)
##define LABEL_PTR(x) &&LABEL(x)
#static void const* const*
#tx_runops(pTHX_ tx_state_t* const st) {
#    static const void* const ops_address_table[] = {
H

pop @ops; # "end"

foreach my $op(@ops) {
    say "        LABEL_PTR($op->[0]),";
}

print code <<'H';
#        LABEL_PTR(end)
#    }; /* end of ops_address_table */
#    if(UNLIKELY(st == NULL)) {
#        return ops_address_table;
#    }
#
#    goto *(st->pc->exec_code); /* start */
#
#    /* dispatch */
H

foreach my $op(@ops) {
    print code sprintf <<'H', @{$op};
#    LABEL(%1$-20s): TXCODE_%1$-20s(aTHX_ st); goto *(st->pc->exec_code);
H
}


print code << 'H';
#    LABEL(end): TXCODE_end(aTHX_ st);
#    return NULL;
#} /* end of tx_runops() */
##undef LABEL
##undef LABEL_PTR
##endif /* TX_DIRECT_THREADED_CODE */

H
