
/********************************************
da.c

libmawk changes (C) 2009-2010, Tibor 'Igor2' Palinkas;
based on mawk code coming with the below copyright:

copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the AWK programming language.

Mawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/

/*  disassemble code */

#include  "mawk.h"
#include  <stdio.h>

#include  "code.h"
#include  "bi_funct.h"
#include  "repl.h"
#include  "field.h"
#include  "num.h"
#include  "f2d.h"

static const struct sc {
	char op;
	char *name;
} simple_code[] = { /* read-only */
	{_RANGE_STOP,"range_stop"},
	{FE_PUSHA,   "fe_pusha"},
	{FE_PUSHI,   "fe_pushi"},
	{A_TEST,     "a_test"},
	{A_DEL,      "a_del"},
	{DEL_A,      "del_a"},
	{POP_AL,     "pop_al"},
	{_POP,       "pop"},
	{_ADD,       "add"},
	{_SUB,       "sub"},
	{_MUL,       "mul"},
	{_DIV,       "div"},
	{_MOD,       "mod"},
	{_POW,       "pow"},
	{_NOT,       "not"},
	{_UMINUS,    "uminus"},
	{_UPLUS,     "uplus"},
	{_TEST,      "mawk_test"},
	{_CAT,       "cat"},
	{_ASSIGN,    "assign"},
	{_ADD_ASG,   "add_asg"},
	{_SUB_ASG,   "sub_asg"},
	{_MUL_ASG,   "mul_asg"},
	{_DIV_ASG,   "div_asg"},
	{_MOD_ASG,   "mod_asg"},
	{_POW_ASG,   "pow_asg"},
	{NF_PUSHI,   "nf_pushi"},
	{F_ASSIGN,   "f_assign"},
	{F_ADD_ASG,  "f_add_asg"},
	{F_SUB_ASG,  "f_sub_asg"},
	{F_MUL_ASG,  "f_mul_asg"},
	{F_DIV_ASG,  "f_div_asg"},
	{F_MOD_ASG,  "f_mod_asg"},
	{F_POW_ASG,  "f_pow_asg"},
	{_POST_INC,  "post_inc"},
	{_POST_DEC,  "post_dec"},
	{_PRE_INC,   "pre_inc"},
	{_PRE_DEC,   "pre_dec"},
	{F_POST_INC, "f_post_inc"},
	{F_POST_DEC, "f_post_dec"},
	{F_PRE_INC,  "f_pre_inc"},
	{F_PRE_DEC,  "f_pre_dec"},
	{_EQ,        "eq"},
	{_NEQ,       "neq"},
	{_LT,        "lt"},
	{_LTE,       "lte"},
	{_GT,        "gt"},
	{_GTE,       "gte"},
	{_MATCH2,    "match2"},
	{_EXIT,      "exit"},
	{_EXIT0,     "exit0"},
	{_NEXT,      "next"},
	{_RET,       "ret"},
	{_RET0,      "ret0"},
	{_OMAIN,     "omain"},
	{_JMAIN,     "jmain"},
	{OL_GL,      "ol_gl"},
	{OL_GL_NR,   "ol_gl_nr"},
	{_ASSIGN_ARR,   "assign_arr"},
	{_ADD_ASG_ARR,  "add_asg_arr"},
	{_SUB_ASG_ARR,  "sub_asg_arr"},
	{_MUL_ASG_ARR,  "mul_asg_arr"},
	{_DIV_ASG_ARR,  "div_asg_arr"},
	{_MOD_ASG_ARR,  "mod_asg_arr"},
	{_POW_ASG_ARR,  "pow_asg_arr"},
	{_POST_INC_ARR, "post_inc_arr"},
	{_POST_DEC_ARR, "post_dec_arr"},
	{_PRE_INC_ARR,  "pre_inc_arr"},
	{_PRE_DEC_ARR,  "pre_dec_arr"},
	{_HALT,      NULL}
};

static const char *jfmt = "%s%s%03d\n";	/* read-only */
	 /* format to print jumps */
static const char *tab2 = "\t\t";	/* read-only */

void mawk_da(mawk_state_t *MAWK, INST *start, void *fp_)
{
	mawk_cell_t *cp;
	FILE *fp = fp_;
	register INST *p = start;
	const char *name;

	while (p->op != _HALT) {
		/* print the relative code address (label) */
		fprintf(fp, "%03d ", p - start);

		switch (p++->op) {

		case _PUSHC:
			cp = (mawk_cell_t *) p++->ptr;
			switch (cp->type) {
			case C_RE:
				fprintf(fp, "pushc\t<<%p>>\t/%s/\n", cp->ptr, mawk_re_uncompile(MAWK, cp->ptr));
				break;

			case C_SPACE:
				fprintf(fp, "pushc\tspace split\n");
				break;

			case C_SNULL:
				fprintf(fp, "pushc\tnull split\n");
				break;
			case C_REPL:
				fprintf(fp, "pushc\trepl\t%s\n", mawk_repl_uncompile(MAWK, cp));
				break;
			case C_REPLV:
				fprintf(fp, "pushc\treplv\t%s\n", mawk_repl_uncompile(MAWK, cp));
				break;

			default:
				fprintf(fp, "pushc\tWEIRD\n");;
				break;
			}
			break;

		case _PUSHD:
			fprintf(fp, "pushd\t" NUM_FMT_DA "\n", *(mawk_num_t *) p++->ptr);
			break;
		case _PUSHS:
			{
				mawk_string_t *sval = (mawk_string_t *) p++->ptr;
				fprintf(fp, "pushs\t\"%s\"\n", sval->str);
				break;
			}

		case _MATCH0:
		case _MATCH1:
			fprintf(fp, "match%d\t<<%p>>\t/%s/\n", p[-1].op == _MATCH1, p->ptr, mawk_re_uncompile(MAWK, p->ptr));
			p++;
			break;

		case _PUSHA:
			fprintf(fp, "pusha\t%s\n", mawk_reverse_find(MAWK, ST_VAR, &p++->ptr));
			break;

		case _PUSHI:
			cp = (mawk_cell_t *) p++->ptr;
			if (cp == MAWK->field)
				fprintf(fp, "pushi\t$0\n");
			else if (cp == &MAWK->fs_shadow)
				fprintf(fp, "pushi\t@fs_shadow\n");
			else {
				if (cp > MAWK_NF && cp <= LAST_PFIELD)
					name = mawk_reverse_find(MAWK, ST_FIELD, &cp);
				else
					name = mawk_reverse_find(MAWK, ST_VAR, &cp);

				fprintf(fp, "pushi\t%s\n", name);
			}
			break;

		case L_PUSHA:
			fprintf(fp, "l_pusha\t%ld\n", (long)p++->op);
			break;

		case L_PUSHI:
			fprintf(fp, "l_pushi\t%ld\n", (long)p++->op);
			break;

		case LAE_PUSHI:
			fprintf(fp, "lae_pushi\t%ld\n", (long)p++->op);
			break;

		case LAE_PUSHA:
			fprintf(fp, "lae_pusha\t%ld\n", (long)p++->op);
			break;

		case LAE_PUSHA_WRARR:
			fprintf(fp, "lae_pusha_wr\t%ld\n", (long)p++->op);
			break;

		case LA_PUSHA:
			fprintf(fp, "la_pusha\t%ld\n", (long)p++->op);
			break;

		case F_PUSHA:
			cp = (mawk_cell_t *) p++->ptr;
			if (cp >= MAWK_NF && cp <= LAST_PFIELD)
				fprintf(fp, "f_pusha\t%s\n", mawk_reverse_find(MAWK, ST_FIELD, &cp));
			else
				fprintf(fp, "f_pusha\t$%d\n", mawk_field_addr_to_index(MAWK, cp));
			break;

		case F_PUSHI:
			p++;
			fprintf(fp, "f_pushi\t$%ld\n", (long)p++->op);
			break;

		case AE_PUSHA:
			fprintf(fp, "ae_pusha\t%s\n", mawk_reverse_find(MAWK, ST_ARRAY, &p++->ptr));
			break;

		case AE_PUSHA_WRARR:
			fprintf(fp, "ae_pusha_wr\t%s\n", mawk_reverse_find(MAWK, ST_ARRAY, &p++->ptr));
			break;

		case AE_PUSHI:
			fprintf(fp, "ae_pushi\t%s\n", mawk_reverse_find(MAWK, ST_ARRAY, &p++->ptr));
			break;

		case A_PUSHA:
			fprintf(fp, "a_pusha\t%s\n", mawk_reverse_find(MAWK, ST_ARRAY, &p++->ptr));
			break;

		case _PUSHINT:
			fprintf(fp, "pushint\t%ld\n", (long)p++->op);
			break;

		case _BUILTIN:
			fprintf(fp, "%s\n", mawk_find_bi_name((PF_CP) mawk_d2f(p++->ptr)));
			break;

		case _PRINT:
			fprintf(fp, "%s\n", (PF_CP) mawk_d2f(p++->ptr) == mawk_bi_printf ? "printf" : "print");
			break;

		case _JMP:
			fprintf(fp, jfmt, "jmp", tab2, (p - start) + p->op);
			p++;
			break;

		case _JNZ:
			fprintf(fp, jfmt, "jnz", tab2, (p - start) + p->op);
			p++;
			break;

		case _JZ:
			fprintf(fp, jfmt, "jz", tab2, (p - start) + p->op);
			p++;
			break;

		case _LJZ:
			fprintf(fp, jfmt, "ljz", tab2, (p - start) + p->op);
			p++;
			break;

		case _LJNZ:
			fprintf(fp, jfmt, "ljnz", tab2 + 1, (p - start) + p->op);
			p++;
			break;

		case SET_ALOOP:
			fprintf(fp, "set_al\t%03d\n", p + p->op - start);
			p++;
			break;

		case ALOOP:
			fprintf(fp, "aloop\t%03ld\n", (long)(p - start + p->op));
			p++;
			break;

		case A_CAT:
			fprintf(fp, "a_cat\t%ld\n", (long)p++->op);
			break;

		case _CALL:
			fprintf(fp, "call\t%s\t%ld\n", ((FBLOCK *) p->ptr)->name, (long)p[1].op);
			p += 2;
			break;

		case _RANGE_CHK:
			fprintf(fp, "range\t%03ld %03ld %03ld\n",
							/* label for pat2, action, follow */
							(long)(p - start + p[1].op), (long)(p - start + p[2].op), (long)(p - start + p[3].op));
			p += 4;
			break;

		case LOCATION:
			fprintf(fp, "(location)\t%ld\n", (long)p[0].op);
			p++;
			break;

		default:
			{
				const struct sc *q = simple_code;
				int k = (p - 1)->op;

				while (q->op != _HALT && q->op != k)
					q++;

				if (q->op != _HALT)
					fprintf(fp, "%s\n", q->name);
				else
					fprintf(fp, "bad instruction %d\n", k);
			}
			break;
		}
	}
	fflush(fp);
}

int mawk_fdump(mawk_state_t * MAWK)
{
	register struct mawk_fdump *p, *q = MAWK->fdump_list;
	int dumped = 0;

	while (q) {
		p = q;
		q = p->link;
		if (dumped)
			fprintf(stdout, "\n");
		fprintf(stdout, "function %s\n", p->fbp->name);
		mawk_da(MAWK, p->fbp->code, stdout);
		dumped++;
	}
	return dumped;
}

static void mawk_symdump(mawk_state_t * MAWK, int verbose)
{
	HASHNODE *p;
	int i;
	for (i = 0; i < HASH_PRIME; i++) {
		p = MAWK->hash_table[i];
		while (p) {
			switch (p->symtab.type) {
				case ST_NONE:
					break;
				case ST_VAR:       fprintf(stdout, "%s var\n", p->symtab.name); break;
				case ST_KEYWORD:   if (verbose) fprintf(stdout, "%s keyword\n", p->symtab.name); break;
				case ST_BUILTIN:   if (verbose) fprintf(stdout, "%s builtin\n", p->symtab.name); break;
				case ST_ARRAY:     fprintf(stdout, "%s array\n", p->symtab.name); break;
				case ST_FIELD:     if (verbose) fprintf(stdout, "%s field\n", p->symtab.name); break;
				case ST_FUNCT:     if (verbose) fprintf(stdout, "%s funct\n", p->symtab.name); break;
				case ST_NR:        if (verbose) fprintf(stdout, "%s <NR>\n", p->symtab.name); break;
				case ST_LENGTH:    if (verbose) fprintf(stdout, "%s <length>\n", p->symtab.name); break;
				case ST_LOCAL_NONE:
				case ST_LOCAL_VAR:
				case ST_LOCAL_ARRAY:
					fprintf(stdout, "%s ?local?\n", p->symtab.name); break;
				case ST_C_FUNCTION: if (verbose) fprintf(stdout, "%s C-function\n", p->symtab.name); break;
			}
			p = p->link;
		}
	}
}

void mawk_dump_code_text(mawk_state_t * MAWK)
{
	int fdmp;

	/* dumps all user functions */
	fdmp = mawk_fdump(MAWK);

	if (MAWK->begin_start) {
		if (fdmp)
			fprintf(stdout, "\n");
		fprintf(stdout, "BEGIN\n");
		mawk_da(MAWK, MAWK->begin_start, stdout);
		fdmp++;
	}

	if (MAWK->main_start) {
		if (fdmp)
			fprintf(stdout, "\n");
		fprintf(stdout, "MAIN\n");
		mawk_da(MAWK, MAWK->main_start, stdout);
		fdmp++;
	}

	if (MAWK->end_start) {
		if (fdmp)
			fprintf(stdout, "\n");
		fprintf(stdout, "END\n");
		mawk_da(MAWK, MAWK->end_start, stdout);
		fdmp++;
	}
}

void mawk_dump_sym_text(mawk_state_t * MAWK)
{
	fprintf(stdout, "SYMBOLS (global)\n");
	mawk_symdump(MAWK, 0);

	(void)mawk_f2d(NULL); /* suppress compiler warning on unused func */
}
