/*	ccop.c
 *
 *	Copyright (c) 1996 Malcolm Beattie
 *
 *	You may distribute under the terms of either the GNU General Public
 *	License or the Artistic License, as specified in the README file.
 *
 */

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

static char *opclassnames[] = {
    "B::NULL",
    "B::OP",
    "B::UNOP",
    "B::BINOP",
    "B::LOGOP",
    "B::CONDOP",
    "B::LISTOP",
    "B::PMOP",
    "B::SVOP",
    "B::GVOP",
    "B::PVOP",
    "B::CVOP",
    "B::LOOP",
    "B::COP"	
};

static opclass
cc_baseop(o)
OP *o;
{
    return OPc_BASEOP;
}

static opclass
cc_unop(o)
OP *o;
{
    return OPc_UNOP;
}

static opclass
cc_binop(o)
OP *o;
{
    return OPc_BINOP;
}

static opclass
cc_logop(o)
OP *o;
{
    return OPc_LOGOP;
}

static opclass
cc_condop(o)
OP *o;
{
    return OPc_CONDOP;
}

static opclass
cc_listop(o)
OP *o;
{
    return OPc_LISTOP;
}

static opclass
cc_pmop(o)
OP *o;
{
    return OPc_PMOP;
}

static opclass
cc_svop(o)
OP *o;
{
    return OPc_SVOP;
}

static opclass
cc_gvop(o)
OP *o;
{
    return OPc_GVOP;
}

static opclass
cc_pvop(o)
OP *o;
{
    return OPc_PVOP;
}

static opclass
cc_cvop(o)
OP *o;
{
    return OPc_CVOP;
}

static opclass
cc_loop(o)
OP *o;
{
    return OPc_LOOP;
}

static opclass
cc_cop(o)
OP *o;
{
    return OPc_COP;
}

/* Nullified ops with children still need to be able to find o->op_first */
static opclass
cc_nullop(o)
OP *o;
{
    return ((o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP);
}

static opclass
cc_stub(o)
OP *o;
{
    warn("compiler stub for %s, assuming BASEOP\n", ppnames[o->op_type]);
    return OPc_BASEOP;		/* XXX lie */
}

/*
 * UNI(OP_foo) in toke.c returns token UNI or FUNC1 depending on whether
 * bare parens were seen. perly.y uses OPf_SPECIAL to signal whether an
 * OP or an UNOP was chosen.
 * Frederic.Chauveau@pasteur.fr says we need to check for OPf_KIDS too.
 */
static opclass
cc_baseop_or_unop(o)
OP *o;
{
    return ((o->op_flags & OPf_SPECIAL) ? OPc_BASEOP :
	    (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP);
}

/*
 * The file stat OPs are created via UNI(OP_foo) in toke.c but use
 * the OPf_REF flag to distinguish between OP types instead of the
 * usual OPf_SPECIAL flag. As usual, if OPf_KIDS is set, then we
 * return OPc_UNOP so that walkoptree can find our children. If
 * OPf_KIDS is not set then we check OPf_REF. Without OPf_REF set
 * (no argument to the operator) it's an OP; with OPf_REF set it's
 * a GVOP (and op_gv is the GV for the filehandle argument).
 */
static opclass
cc_filestatop(o)
OP *o;
{
    return ((o->op_flags & OPf_KIDS) ? OPc_UNOP :
	    (o->op_flags & OPf_REF) ? OPc_GVOP : OPc_BASEOP);
}

/*
 * next, last, redo, dump and goto use OPf_SPECIAL to indicate that a
 * label was omitted (in which case it's a BASEOP) or else a term was
 * seen. In this last case, all except goto are definitely PVOP but goto
 * is either a PVOP (with an ordinary constant label), an UNOP with
 * OPf_STACKED (with a non-constant non-sub) or an UNOP for OP_REFGEN
 * (with goto &sub) in which case OPf_STACKED also seems to get set.
 */

static opclass
cc_loopexop(o)
OP *o;
{
    if (o->op_flags & OPf_STACKED)
	return OPc_UNOP;
    else if (o->op_flags & OPf_SPECIAL)
	return OPc_BASEOP;
    else
	return OPc_PVOP;
}

static opclass
cc_sassign(o)
OP *o;
{
    return ((o->op_private & OPpASSIGN_BACKWARDS) ? OPc_UNOP : OPc_BINOP);
}

static opclass (*ccopaddr[])_((OP *o)) = {
	cc_nullop,		/* null */
	cc_baseop,		/* stub */
	cc_baseop_or_unop,	/* scalar */
	cc_baseop,		/* pushmark */
	cc_baseop,		/* wantarray */
	cc_svop,		/* const */
	cc_gvop,		/* gvsv */
	cc_gvop,		/* gv */
	cc_binop,		/* gelem */
	cc_baseop,		/* padsv */
	cc_baseop,		/* padav */
	cc_baseop,		/* padhv */
	cc_baseop,		/* padany */
	cc_pmop,		/* pushre */
	cc_unop,		/* rv2gv */
	cc_unop,		/* rv2sv */
	cc_unop,		/* av2arylen */
	cc_unop,		/* rv2cv */
	cc_svop,		/* anoncode */
	cc_baseop_or_unop,	/* prototype */
	cc_unop,		/* refgen */
	cc_unop,		/* srefgen */
	cc_baseop_or_unop,	/* ref */
	cc_listop,		/* bless */
	cc_baseop_or_unop,	/* backtick */
	cc_listop,		/* glob */
	cc_baseop_or_unop,	/* readline */
	cc_stub,		/* rcatline */
	cc_unop,		/* regcmaybe */
	cc_logop,		/* regcomp */
	cc_pmop,		/* match */
	cc_pmop,		/* subst */
	cc_logop,		/* substcont */
	cc_pvop,		/* trans */
	cc_sassign,		/* sassign */
	cc_binop,		/* aassign */
	cc_baseop_or_unop,	/* chop */
	cc_baseop_or_unop,	/* schop */
	cc_baseop_or_unop,	/* chomp */
	cc_baseop_or_unop,	/* schomp */
	cc_baseop_or_unop,	/* defined */
	cc_baseop_or_unop,	/* undef */
	cc_baseop_or_unop,	/* study */
	cc_baseop_or_unop,	/* pos */
	cc_unop,		/* preinc */
	cc_unop,		/* i_preinc */
	cc_unop, 		/* predec */
	cc_unop,		/* i_predec */
	cc_unop,		/* postinc */
	cc_unop,		/* i_postinc */
	cc_unop,		/* postdec */
	cc_unop,		/* i_postdec */
	cc_binop,		/* pow */
	cc_binop,		/* multiply */
	cc_binop,		/* i_multiply */
	cc_binop,		/* divide */
	cc_binop,		/* i_divide */
	cc_binop,		/* modulo */
	cc_binop,		/* i_modulo */
	cc_binop,		/* repeat */
	cc_binop,		/* add */
	cc_binop,		/* i_add */
	cc_binop,		/* subtract */
	cc_binop,		/* i_subtract */
	cc_binop,		/* concat */
	cc_listop,		/* stringify */
	cc_binop,		/* left_shift */
	cc_binop,		/* right_shift */
	cc_binop,		/* lt */
	cc_binop,		/* i_lt */
	cc_binop,		/* gt */
	cc_binop,		/* i_gt */
	cc_binop,		/* le */
	cc_binop,		/* i_le */
	cc_binop,		/* ge */
	cc_binop,		/* i_ge */
	cc_binop,		/* eq */
	cc_binop,		/* i_eq */
	cc_binop,		/* ne */
	cc_binop,		/* i_ne */
	cc_binop,		/* ncmp */
	cc_binop,		/* i_ncmp */
	cc_binop,		/* slt */
	cc_binop,		/* sgt */
	cc_binop,		/* sle */
	cc_binop,		/* sge */
	cc_binop,		/* seq */
	cc_binop,		/* sne */
	cc_binop,		/* scmp */
	cc_binop,		/* bit_and */
	cc_binop,		/* bit_xor */
	cc_binop,		/* bit_or */
	cc_unop,    		/* negate */
	cc_unop,		/* i_negate */
	cc_unop,		/* not */
	cc_unop,		/* complement */
	cc_listop,		/* atan2 */
	cc_baseop_or_unop,	/* sin */
	cc_baseop_or_unop,	/* cos */
	cc_baseop_or_unop,	/* rand */
	cc_baseop_or_unop,	/* srand */
	cc_baseop_or_unop,	/* exp */
	cc_baseop_or_unop,	/* log */
	cc_baseop_or_unop,	/* sqrt */
	cc_baseop_or_unop,	/* int */
	cc_baseop_or_unop,	/* hex */
	cc_baseop_or_unop,	/* oct */
	cc_baseop_or_unop,	/* abs */
	cc_baseop_or_unop,	/* length */
	cc_listop,		/* substr */
	cc_listop,		/* vec */
	cc_listop,		/* index */
	cc_listop,		/* rindex */
	cc_listop,		/* sprintf */
	cc_listop,		/* formline */
	cc_baseop_or_unop,	/* ord */
	cc_baseop_or_unop,	/* chr */
	cc_listop,		/* crypt */
	cc_baseop_or_unop,	/* ucfirst */
	cc_baseop_or_unop,	/* lcfirst */
	cc_baseop_or_unop,	/* uc */
	cc_baseop_or_unop,	/* lc */
	cc_baseop_or_unop,	/* quotemeta */
	cc_unop,		/* rv2av */
	cc_gvop,		/* aelemfast */
	cc_binop,         	/* aelem */
	cc_listop,		/* aslice */
	cc_baseop_or_unop,	/* each */
	cc_baseop_or_unop,	/* values */
	cc_baseop_or_unop,	/* keys */
	cc_baseop_or_unop,	/* delete */
	cc_baseop_or_unop,	/* exists */
	cc_unop,		/* rv2hv */
	cc_binop,		/* helem */
	cc_listop,		/* hslice */
	cc_listop,		/* unpack */
	cc_listop,		/* pack */
	cc_listop,		/* split */
	cc_listop,		/* join */
	cc_listop,		/* list */
	cc_binop,		/* lslice */
	cc_listop,		/* anonlist */
	cc_listop,		/* anonhash */
	cc_listop,		/* splice */
	cc_listop,		/* push */
	cc_baseop_or_unop,	/* pop */
	cc_baseop_or_unop,	/* shift */
	cc_listop,		/* unshift */
	cc_listop,		/* sort */
	cc_listop,		/* reverse */
	cc_listop,		/* grepstart */
	cc_logop,		/* grepwhile */
	cc_listop,		/* mapstart */
	cc_logop,		/* mapwhile */
	cc_condop,		/* range */
	cc_unop,		/* flip */
	cc_unop,		/* flop */
	cc_logop,		/* and */
	cc_logop,		/* or */
	cc_logop,		/* xor */
	cc_condop,		/* cond_expr */
	cc_logop,		/* andassign */
	cc_logop,		/* orassign */
	cc_unop,		/* method */
	cc_unop,		/* entersub */
	cc_unop,		/* leavesub */
	cc_baseop_or_unop,	/* caller */
	cc_listop,		/* warn */
	cc_listop,		/* die */
	cc_baseop_or_unop,	/* reset */
	cc_listop,		/* lineseq */
	cc_cop,			/* nextstate */
	cc_cop,			/* dbstate */
	cc_baseop,		/* unstack */
	cc_baseop,		/* enter */
	cc_listop,		/* leave */
	cc_listop,		/* scope */
	cc_loop,		/* enteriter */
	cc_baseop,		/* iter */
	cc_loop,		/* enterloop */
	cc_binop,		/* leaveloop */
	cc_listop,		/* return */
	cc_loopexop,		/* last */
	cc_loopexop,		/* next */
	cc_loopexop,		/* redo */
	cc_loopexop,		/* dump */
	cc_loopexop,		/* goto */
	cc_baseop_or_unop,	/* exit */
	cc_listop,		/* open */
	cc_baseop_or_unop,	/* close */
	cc_listop,		/* pipe_op */
	cc_baseop_or_unop,	/* fileno */
	cc_baseop_or_unop,	/* umask */
	cc_baseop_or_unop,	/* binmode */
	cc_listop,		/* tie */
	cc_baseop_or_unop,	/* untie */
	cc_baseop_or_unop,	/* tied */
	cc_listop,		/* dbmopen */
	cc_baseop_or_unop,	/* dbmclose */
	cc_listop,		/* sselect */
	cc_listop,		/* select */
	cc_baseop_or_unop,	/* getc */
	cc_listop,		/* read */
	cc_baseop_or_unop,	/* enterwrite */
	cc_unop,		/* leavewrite */
	cc_listop,		/* prtf */
	cc_listop,		/* print */
	cc_listop,		/* sysopen */
	cc_listop,		/* sysread */
	cc_listop,		/* syswrite */
	cc_listop,		/* send */
	cc_listop,		/* recv */
	cc_baseop_or_unop,	/* eof */
	cc_baseop_or_unop,	/* tell */
	cc_listop,		/* seek */
	cc_listop,		/* truncate */
	cc_listop,		/* fcntl */
	cc_listop,		/* ioctl */
	cc_listop,		/* flock */
	cc_listop,		/* socket */
	cc_listop,		/* sockpair */
	cc_listop,		/* bind */
	cc_listop,		/* connect */
	cc_listop,		/* listen */
	cc_listop,		/* accept */
	cc_listop,		/* shutdown */
	cc_listop,		/* gsockopt */
	cc_listop,		/* ssockopt */
	cc_baseop_or_unop,	/* getsockname */
	cc_baseop_or_unop,	/* getpeername */
	cc_filestatop,		/* lstat */
	cc_filestatop,		/* stat */
	cc_filestatop,		/* ftrread */
	cc_filestatop,		/* ftrwrite */
	cc_filestatop,		/* ftrexec */
	cc_filestatop,		/* fteread */
	cc_filestatop,		/* ftewrite */
	cc_filestatop,		/* fteexec */
	cc_filestatop,		/* ftis */
	cc_filestatop,		/* fteowned */
	cc_filestatop,		/* ftrowned */
	cc_filestatop,		/* ftzero */
	cc_filestatop,		/* ftsize */
	cc_filestatop,		/* ftmtime */
	cc_filestatop,		/* ftatime */
	cc_filestatop,		/* ftctime */
	cc_filestatop,		/* ftsock */
	cc_filestatop,		/* ftchr */
	cc_filestatop,		/* ftblk */
	cc_filestatop,		/* ftfile */
	cc_filestatop,		/* ftdir */
	cc_filestatop,		/* ftpipe */
	cc_filestatop,		/* ftlink */
	cc_filestatop,		/* ftsuid */
	cc_filestatop,		/* ftsgid */
	cc_filestatop,		/* ftsvtx */
	cc_filestatop,		/* fttty */
	cc_filestatop,		/* fttext */
	cc_filestatop,		/* ftbinary */
	cc_baseop_or_unop,	/* chdir */
	cc_listop,		/* chown */
	cc_baseop_or_unop,	/* chroot */
	cc_listop,		/* unlink */
	cc_listop,		/* chmod */
	cc_listop,		/* utime */
	cc_listop,		/* rename */
	cc_listop,		/* link */
	cc_listop,		/* symlink */
	cc_baseop_or_unop,	/* readlink */
	cc_listop,		/* mkdir */
	cc_baseop_or_unop,	/* rmdir */
	cc_listop,		/* open_dir */
	cc_baseop_or_unop,	/* readdir */
	cc_baseop_or_unop,	/* telldir */
	cc_listop,		/* seekdir */
	cc_baseop_or_unop,	/* rewinddir */
	cc_baseop_or_unop,	/* closedir */
	cc_baseop,		/* fork */
	cc_baseop,		/* wait */
	cc_listop,		/* waitpid */
	cc_listop,		/* system */
	cc_listop,		/* exec */
	cc_listop,		/* kill */
	cc_baseop,		/* getppid */
	cc_baseop_or_unop,	/* getpgrp */
	cc_listop,		/* setpgrp */
	cc_listop,		/* getpriority */
	cc_listop,		/* setpriority */
	cc_baseop,		/* time */
	cc_baseop,		/* tms */
	cc_baseop_or_unop,	/* localtime */
	cc_baseop_or_unop,	/* gmtime */
	cc_baseop_or_unop,	/* alarm */
	cc_baseop_or_unop,	/* sleep */
	cc_listop,		/* shmget */
	cc_listop,		/* shmctl */
	cc_listop,		/* shmread */
	cc_listop,		/* shmwrite */
	cc_listop,		/* msgget */
	cc_listop,		/* msgctl */
	cc_listop,		/* msgsnd */
	cc_listop,		/* msgrcv */
	cc_listop,		/* semget */
	cc_listop,		/* semctl */
	cc_listop,		/* semop */
	cc_baseop_or_unop,	/* require */
	cc_unop,		/* dofile */
	cc_baseop_or_unop,	/* entereval */
	cc_unop,		/* leaveeval */
	cc_logop,		/* entertry */
	cc_listop,		/* leavetry */
	cc_baseop_or_unop,	/* ghbyname */
	cc_listop,		/* ghbyaddr */
	cc_baseop,		/* ghostent */
	cc_baseop_or_unop,	/* gnbyname */
	cc_listop,		/* gnbyaddr */
	cc_baseop,		/* gnetent */
	cc_baseop_or_unop,	/* gpbyname */
	cc_listop,		/* gpbynumber */
	cc_baseop,		/* gprotoent */
	cc_listop,		/* gsbyname */
	cc_listop,		/* gsbyport */
	cc_baseop,		/* gservent */
	cc_baseop_or_unop,	/* shostent */
	cc_baseop_or_unop,	/* snetent */
	cc_baseop_or_unop,	/* sprotoent */
	cc_baseop_or_unop,	/* sservent */
	cc_baseop,		/* ehostent */
	cc_baseop,		/* enetent */
	cc_baseop,		/* eprotoent */
	cc_baseop,		/* eservent */
	cc_baseop_or_unop,	/* gpwnam */
	cc_baseop_or_unop,	/* gpwuid */
	cc_baseop,		/* gpwent */
	cc_baseop,		/* spwent */
	cc_baseop,		/* epwent */
	cc_baseop_or_unop,	/* ggrnam */
	cc_baseop_or_unop,	/* ggrgid */
	cc_baseop,		/* ggrent */
	cc_baseop,		/* sgrent */
	cc_baseop,		/* egrent */
	cc_baseop,		/* getlogin */
	cc_listop,		/* syscall */
};

opclass
cc_opclass(o)
OP *	o;
{
    return o ? (*ccopaddr[o->op_type])(o) : OPc_NULL;
}

char *
cc_opclassname(o)
OP *	o;
{
    return opclassnames[o ? (*ccopaddr[o->op_type])(o) : OPc_NULL];
}

