view src/ploki/deparse.c @ 12292:d51f2100210c draft

<kspalaiologos> `` cat <<<"asmbf && bfi output.b" > /hackenv/ibin/asmbf
author HackEso <hackeso@esolangs.org>
date Thu, 02 Jan 2020 15:38:21 +0000
parents ac0403686959
children
line wrap: on
line source

#include "config.h"
#include "Str.h"
#include "deparse.h"
#include "expr.h"
#include "main_io.h"
#include "main_label.h"
#include "text.h"
#include "val.h"
#include "venus.h"
#include "xmalloc.h"
#include "zz.h"

#include <ctype.h>
#include <stdio.h>
#include <assert.h>

#if HAVE_VSNPRINTF_P
#define MAKE_LABEL(s, c)                \
do {                                    \
	static unsigned long seq__;         \
	St_init(s);                         \
	St_xprintf(s, "%c%lu", c, seq__++); \
} while (0)
#else
#define MAKE_LABEL(s, c)        \
do {                            \
	static unsigned long seq__; \
	St_init(s);                 \
	St_num(s, seq__++);         \
	St_tac_c(s, c);             \
} while (0)
#endif

ATTR_CONST
static int display(enum t_binop b) {
	switch (b) {
		case B_SPARK_SPOT:      return '!';
		case B_DOUBLE_OH_SEVEN: return '%';
		case B_AMPERSAND:       return '&';
		case B_SPARK:           return '\'';
		case B_SPLAT:           return '*';
		case B_INTERSECTION:    return '+';
		case B_TAIL:            return ',';
		case B_WORM:            return '-';
		case B_SPOT:            return '.';
		case B_SLAT:            return '/';
		case B_TWO_SPOT:        return ':';
		case B_HYBRID:          return ';';
		case B_ANGLE:           return '<';
		case B_HALF_MESH:       return '=';
		case B_RIGHT_ANGLE:     return '>';
		case B_U_TURN:          return '[';
		case B_U_TURN_BACK:     return ']';
		case B_SHARK_FIN:       return '^';
		case B_FLATWORM:        return '_';
		case B_BACKSPARK:       return '`';
		case B_EMBRACE:         return '{';
		case B_SPIKE:           return '|';
		case B_BRACELET:        return '}';
		case B_SQIGGLE:         return '~';
		default: break;
	}
	NOTREACHED;
}

static void dump_str(const String *s) {
	size_t i;

	io_write_m(Out, "\"", 1);
	for (i = 0; i < St_len(s); ++i) {
		const unsigned char c = ST_INDEX(s, i);
		if (c != '\\' && c != '"' && isprint(c)) {
			io_write_m(Out, &c, 1);
		} else {
			io_write_m(Out, "\\", 1);
			switch (c) {
				case '"':
				case '\\': io_write_m(Out, &c , 1); break;
				case '\a': io_write_m(Out, "a", 1); break;
				case '\b': io_write_m(Out, "b", 1); break;
				case '\f': io_write_m(Out, "f", 1); break;
				case '\n': io_write_m(Out, "n", 1); break;
				case '\r': io_write_m(Out, "r", 1); break;
				case '\t': io_write_m(Out, "t", 1); break;
				case '\v': io_write_m(Out, "v", 1); break;
				default:   fprintf(io_fp(Out), "%03o", c); break;
			}
		}
	}
	io_write_m(Out, "\"", 1);
}

static void dump_ko(const struct kork *k) {
	String tmp;
	St_fake(&tmp, (char *)ko_ptr(k), ko_length(k));
	dump_str(&tmp);
}

static void to_id(struct kork *k, size_t n) {
	static char id[] =
		"abcdefghijklmnopqrstuvwxyz"
		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
		"$"
		;
	const size_t base = sizeof id - 1;
	do {
		ko_cat_c(k, id[n % base]);
		n /= base;
	} while (n);
}

static void make_var(struct val *v) {
	static size_t seq;

	ko_decouple(v->ko);
	to_id(v->ko, seq++);
	v->type = V_STR_K;
}

static void dump_expr(const struct expr *e, int inlist) {
	assert(e != NULL);

	switch (e->type) {
		case literE:
			if (V_EXT_P(e->v.val)) {
				fprintf(io_fp(Out), "?%s?", io_name(e->v.val->magic.ext, NULL));
			} else if (V_SUB_P(e->v.val)) {
				fprintf(io_fp(Out), "?CODE(%p)?", (void *)e->v.val->magic.sub);
			} else if (V_STR_P(e->v.val)) {
				dump_ko(e->v.val->ko);
			} else if (V_NUM_P(e->v.val)) {
				if (e->v.val->num < 0.0) {
					fprintf(io_fp(Out), "@NEG %g", -e->v.val->num);
				} else {
					fprintf(io_fp(Out), "%g", e->v.val->num);
				}
			} else {
				#if 0
				io_write_m(Out, "()", 2);
				#endif
			}
			break;

		case varE:
			if (!V_STR_P(e->v.val)) {
				assert(e->v.val->type == V_UNDEF);
				make_var(e->v.val);
			}
			io_write_m(Out, ko_ptr(e->v.val->ko), ko_length(e->v.val->ko));
			break;

		case varhashE: {
			struct val *tmp;
			if (!(tmp = sh_get(e->v.hash, "name", 4))) {
				tmp = v_undef();
				make_var(tmp);
				sh_put(e->v.hash, "name", 4, tmp);
			}
			io_write_m(Out, ko_ptr(tmp->ko), ko_length(tmp->ko));
			io_write_m(Out, "(", 1);
			dump_expr(e->right, 0);
			io_write_m(Out, ")", 1);
			break;
		}

		case symbolE:
			io_write_m(Out, "\\", 1);
			switch (e->op) {
				case S_NUL:                                break;
				case S_ARG:    io_write_m(Out, "@", 1);    break;
				case S_ARGC:   io_write_m(Out, "ARG", 3);  break;

				case S_ARGV:
					io_write_m(Out, "ARG:", 4);
					if (e->right->type == binopE) {
						io_write_m(Out, "(", 1);
						dump_expr(e->right, 0);
						io_write_m(Out, ")", 1);
					} else {
						dump_expr(e->right, 0);
					}
					break;

				case S_ERR:    io_write_m(Out, "!", 1);    break;
				case S_EULER:  io_write_m(Out, "E", 1);    break;
				case S_LUDOLF: io_write_m(Out, "PI", 2);   break;
				case S_MATCH:
					fprintf(io_fp(Out), "%lu", (unsigned long)e->left.bonus);
					break;
				case S_RAND:   io_write_m(Out, "?", 1);    break;
				case S_RESULT: io_write_m(Out, "_", 1);    break;
				case S_STDIN:  io_write_m(Out, "EING", 4); break;
				case S_STDOUT: io_write_m(Out, "AUSG", 4); break;
				case S_STDERR: io_write_m(Out, "FEHL", 4); break;

				default:       NOTREACHED;                 break;
			}
			break;

		case unopE:
			switch (e->op) {
				case F_EXP:    io_write_m(Out, "\\E^", 3); break;
				case F_LOWER:  io_write_m(Out, "\\L" , 2); break;
				case F_QUOTE:  io_write_m(Out, "\\Q" , 2); break;
				case F_RE_ESC: io_write_m(Out, "\\R" , 2); break;
				case F_UPPER:  io_write_m(Out, "\\U" , 2); break;
				case F_MATCH: {
					String tmp;
					io_write_m(Out, "(", 1);
					dump_expr(e->right, 0);
					io_write_m(Out, " ~ ", 3);
					St_init(&tmp);
					re_decompile(e->left.rx, &tmp);
					dump_str(&tmp);
					St_clear(&tmp);
					io_write_m(Out, ")", 1);
					return;
				}

				default:
					io_write_m(Out, "@", 1);
					switch (e->op) {
						case F_CALL:    io_write(Out, &e->left.op->txt);
						case F_NUL:     break;
						case F_ABS:     io_write_m(Out, "ABS", 3);     break;
						case F_ACOS:    io_write_m(Out, "ACOS", 4);    break;
						case F_ASIN:    io_write_m(Out, "ASIN", 4);    break;
						case F_ATAN:    io_write_m(Out, "ATAN", 4);    break;
						case F_ATAN2:   io_write_m(Out, "ATAN2", 5);   break;
						case F_CATCH:   io_write_m(Out, "EVAL", 4);    break;
						case F_CHR:     io_write_m(Out, "CHR", 3);     break;
						case F_COS:     io_write_m(Out, "COS", 3);     break;
						case F_DEFINED: io_write_m(Out, "DEF-P", 5);   break;
						case F_EOF:     io_write_m(Out, "EDD-P", 5);   break;
						case F_ERROR:   io_write_m(Out, "ERR-P", 5);   break;
						case F_FREEZE:  io_write_m(Out, "OMFG", 4);    break;
						case F_GETC:    io_write_m(Out, "GET", 3);     break;
						case F_GETENV:  io_write_m(Out, "ENV", 3);     break;
						case F_GETS:    io_write_m(Out, "LEGS", 4);    break;
						case F_HANG: {
							size_t i;
							String tmp;

							St_init(&tmp);
							for (St_num(&tmp, i = 1);
									ve_findnext(&Venus, &tmp, 0);
									St_num(&tmp, ++i))
								;
							fprintf(io_fp(Out), "(%s)", St_ptr(&tmp));
							St_clear(&tmp);
							break;
						}
						case F_INT:     io_write_m(Out, "INT", 3);     break;
						case F_IO:      io_write_m(Out, "IO-P", 4);    break;
						case F_LENGTH:  io_write_m(Out, "LENGTH", 6);  break;
						case F_LOG10:   io_write_m(Out, "LG", 2);      break;
						case F_LOG:     io_write_m(Out, "LN", 2);      break;
						case F_MOEND:   io_write_m(Out, "+", 1);       break;
						case F_MOSTART: io_write_m(Out, "-", 1);       break;
						case F_NEG:     io_write_m(Out, "NEG", 3);     break;
						case F_NOT:     io_write_m(Out, "NOT", 3);     break;
						case F_NUM:     io_write_m(Out, "NUM", 3);     break;
						case F_OPEN:    io_write_m(Out, "APERS", 5);   break;
						case F_OPENR:   io_write_m(Out, "LAPERS", 6);  break;
						case F_OPENW:   io_write_m(Out, "SAPERS", 6);  break;
						case F_ORD:     io_write_m(Out, "ORD", 3);     break;
						case F_REMOVE:  io_write_m(Out, "REMOVE", 6);  break;
						case F_RENAME:  io_write_m(Out, "RENAEM", 6);  break;
						case F_REVERSE: io_write_m(Out, "REVERSE", 7); break;
						case F_SEEK:    io_write_m(Out, "SUCH", 4);    break;
						case F_SIN:     io_write_m(Out, "SIN", 3);     break;
						case F_SQRT:    io_write_m(Out, "SPQR", 4);    break;
						case F_STR:     io_write_m(Out, "STR", 3);     break;
						case F_TAN:     io_write_m(Out, "TAN", 3);     break;
						case F_TELL:    io_write_m(Out, "SAG", 3);     break;
						case F_TYPEOF:  io_write_m(Out, "TYPE OF", 7); break;
						default:
							NOTREACHED;
							break;
					}
					break;
			}
			if (e->right->type != unopE || e->op == F_NUL) {
				io_write_m(Out, "(", 1);
				dump_expr(e->right, 0);
				io_write_m(Out, ")", 1);
			} else {
				io_write_m(Out, " ", 1);
				dump_expr(e->right, 0);
			}
			break;

		case binopE:
			dump_expr(e->left.expr, 0);

			if (e->op == B_XMATCH) {
				io_write_m(Out, " ?o~ ", 5);
			} else {
				fprintf(io_fp(Out), "%s%c ", &" "[e->op == B_TAIL], display(e->op));
			}

			if (e->right->type == binopE) {
				io_write_m(Out, "(", 1);
				dump_expr(e->right, 0);
				io_write_m(Out, ")", 1);
			} else {
				dump_expr(e->right, 0);
			}
			break;

		case listE:
			if (!inlist) {
				io_write_m(Out, "#<", 2);
			}
			if (e->right) {
				io_write_m(Out, "(", 1);
				dump_expr(e->right, 0);
				io_write_m(Out, ")", 1);
				if (e->left.expr) {
					io_write_m(Out, " ", 1);
					dump_expr(e->left.expr, 1);
				}
			}
			if (!inlist) {
				io_write_m(Out, "#>", 2);
			}
			break;

	}
}

static void dump_op(struct op *op) {
	switch (op->type) {
		case OP_NOP:
			io_write_m(Out, "REM", 3);
			break;

		case OP_SET_VAL:
		case OP_ASSIGN:
			io_write_m(Out, "LET ", 4);
			if (op->arh.expr) {
				dump_expr(op->arh.expr, 0);
				io_write_m(Out, " ", 1);
				dump_expr(op->arg, 0);
			} else {
				io_write_m(Out, "(", 1);
				dump_expr(op->arg, 0);
				io_write_m(Out, ")", 1);
			}
			break;

		case OP_CALL:
			if (op->arh.op) {
				fprintf(io_fp(Out), "%s ", St_ptr(&op->arh.op->txt));
				dump_expr(op->arg, 0);
			} else {
				io_write_m(Out, "LET () ", 7);
				dump_expr(op->arg, 0);
				if (op->next) {
					io_write_m(Out, "\nEND", 4);
				}
			}
			break;

		case OP_CALL_BACK:
			io_write_m(Out, "ABRUF (", 7);
			dump_expr(op->arh.expr, 0);
			io_write_m(Out, ") ", 2);
			dump_expr(op->arg, 0);
			break;

		case OP_CALL_DYN:
			io_write_m(Out, "ANRUF ", 6);
			dump_expr(op->arg, 0);
			break;

		case OP_CLOSE:
			io_write_m(Out, "CLAUDS ", 7);
			dump_expr(op->arg, 0);
			break;

		case OP_ELSE:
			NOTREACHED;
			break;

		case OP_EXIT:
			if (op->arg->type == literE && !op->arg->v.val->type) {
				io_write_m(Out, "END", 3);
			} else {
				io_write_m(Out, "END ", 4);
				dump_expr(op->arg, 0);
			}
			break;

		case OP_FI:
			NOTREACHED;
			break;

		case OP_FLUSH:
			io_write_m(Out, "FLUSH ", 6);
			dump_expr(op->arg, 0);
			break;

		case OP_GOBACK:
			io_write_m(Out, "GOFOR ", 6);
			dump_expr(op->arg, 0);
			break;

		case OP_GOTO:
			io_write_m(Out, "GOTO ", 5);
			dump_expr(op->arg, 0);
			break;

		case OP_HANG:
			if (St_ptr(&op->txt)) {
				fprintf(io_fp(Out), "NEXT %s", St_ptr(&op->txt));
			} else {
				String tmp;
				MAKE_LABEL(&tmp, '*');
				fprintf(io_fp(Out), "FOR %s NEXT %s", St_ptr(&tmp), St_ptr(&tmp));
				St_clear(&tmp);
			}
			break;

		case OP_IF:
			if (op->arg->type == unopE && op->arg->op == F_NOT) {
				io_write_m(Out, "IF ", 3);
				dump_expr(op->arg->right, 0);
			} else {
				io_write_m(Out, "IF @NOT ", 8);
				if (op->arg->type == binopE) {
					io_write_m(Out, "(", 1);
					dump_expr(op->arg, 0);
					io_write_m(Out, ")", 1);
				} else {
					dump_expr(op->arg, 0);
				}
			}

			if (op->next) {
				fprintf(io_fp(Out), "\n  NEXT %s\nFI", St_ptr(&op->next->txt));
			} else {
				io_write_m(Out, "\n  END\nFI", 9);
			}
			break;

		case OP_MODIFY:
			io_write_m(Out, "LET ", 4);
			dump_expr(op->arh.expr, 0);
			if (op->arh.expr->op == B_XMATCH) {
				io_write_m(Out, " ?o~= ", 6);
			} else {
				fprintf(io_fp(Out), " %c= ", display(op->arh.expr->op));
			}
			dump_expr(op->arg, 0);
			break;

		case OP_PRINT:
			io_write_m(Out, "WUNT ", 5);
			if (op->arh.expr) {
				dump_expr(op->arh.expr, 0);
				io_write_m(Out, " ", 1);
				dump_expr(op->arg, 0);
			} else if (op->arg->type != binopE) {
				dump_expr(op->arg, 0);
			} else {
				io_write_m(Out, "(", 1);
				dump_expr(op->arg, 0);
				io_write_m(Out, ")", 1);
			}
			break;

		case OP_PUTC:
			io_write_m(Out, "SET ", 4);
			dump_expr(op->arg, 0);
			break;

		case OP_RESET:
			io_write_m(Out, "RESET ", 6);
			dump_expr(op->arg, 0);
			break;

		case OP_RETURN:
			io_write_m(Out, "KTHX ", 5);
			dump_expr(op->arg, 0);
			break;

		case OP_SYSTEM:
			io_write_m(Out, "# ", 2);
			dump_expr(op->arg, 0);
			break;

		case OP_TEMP:
			io_write_m(Out, "LEET ", 5);
			dump_expr(op->arh.expr, 0);
			io_write_m(Out, " ", 1);
			dump_expr(op->arg, 0);
			break;

		case OP_THROW:
			io_write_m(Out, "IACS ", 5);
			dump_expr(op->arg, 0);
			break;

		default:
			NOTREACHED;
			break;
	}
}

static struct {
	size_t length;
	size_t size;
	struct op **ops;
} seen;

#define PUSH_SEEN(p) do {                                                \
	if (seen.length >= seen.size) {                                      \
		seen.ops = xrealloc(seen.ops, seen.size *= 2);                   \
	}                                                                    \
	seen.ops[seen.length++] = (p);                                       \
} while (0)

static int walk(const struct expr *e) {
	for (;;) {
		switch (e->type) {
			case literE:
			case symbolE:
				return 0;

			case varE:
				return 0;

			case varhashE:
				e = e->right;
				continue;

			case unopE:
				if (e->op == F_NUL) {
					return 1;
				} else if (e->op == F_CALL && !St_ptr(&e->left.op->txt)) {
					MAKE_LABEL(&e->left.op->txt, 'f');
					PUSH_SEEN(e->left.op);
				}
				e = e->right;
				continue;

			case binopE:
				return walk(e->left.expr) | walk(e->right);

			case listE:
				return e->right && (walk(e->right) | (e->left.expr && walk(e->left.expr)));
		}
	}
}

static void do_stuff(struct op *op) {
	for (; op && !op->line; op = op->next) {
		op->line = 1;

		if (St_ptr(&op->txt)) {
			fprintf(io_fp(Out), "FOR %s ", St_ptr(&op->txt));
			if (op->type != OP_NOP) {
				dump_op(op);
			} else {
				io_write_m(Out, "REM", 4);
			}
			io_write_m(Out, "\n", 1);
		} else {
			dump_op(op);
			#if 0
			if (op->type != OP_NOP)
			#endif
				io_write_m(Out, "\n", 1);
		}

		if (op->type == OP_IF) {
			if (op->arh.op) {
				if (op->arh.op->line) {
					fprintf(io_fp(Out), "NEXT %s\n", St_ptr(&op->arh.op->txt));
				} else {
					do_stuff(op->arh.op);
				}
			}
		} else if (op->type == OP_CALL) {
			if (op->arh.op && !op->arh.op->line) {
				PUSH_SEEN(op->arh.op);
			}
		} else if (op->type == OP_HANG) {
			return;
		}

		if (op->next) {
			if (op->next->line && op->type != OP_IF && op->type != OP_RETURN) {
				fprintf(io_fp(Out), "NEXT %s\n", St_ptr(&op->next->txt));
			}
		} else if (
				op->type != OP_EXIT &&
				op->type != OP_CALL &&
				op->type != OP_CALL_BACK &&
				op->type != OP_CALL_DYN &&
				op->type != OP_HANG &&
				op->type != OP_RETURN
				) {
			io_write_m(Out, "END\n", 4);
		}
	}
}

enum {MAGIC = 23};
void deparse(const struct text *t) {
	size_t i;
	int computed_jumps;

	computed_jumps = 0;
	seen.ops = xmalloc(seen.size = MAGIC, sizeof *seen.ops);
	seen.length = 0;

	for (i = 0; i < t->length; ++i) {
		struct op *op = t->start[i];

		op->line = 0;

		if (
				op->type == OP_GOBACK ||
				op->type == OP_GOTO ||
				op->type == OP_CALL_DYN
		   ) {
			walk(op->arg);
			computed_jumps |= 1;
		} else if (op->type == OP_CALL_BACK) {
			walk(op->arh.expr);
			walk(op->arg);
			computed_jumps |= 1;
		} else if (OP_1ARG_P(op->type)) {
			computed_jumps |= walk(op->arg);
			if (op->type == OP_CALL && op->arh.op) {
				if (!St_ptr(&op->arh.op->txt)) {
					MAKE_LABEL(&op->arh.op->txt, 'c');
				}
			}
		} else if (OP_2ARG_P(op->type)) {
			if (op->arh.expr) {
				computed_jumps |= walk(op->arh.expr);
			}
			computed_jumps |= walk(op->arg);
		}

		if (
				op->next &&
				(op->next != t->start[i + 1] || op->type == OP_CALL) &&
				!St_ptr(&op->next->txt)
		   ) {
			MAKE_LABEL(&op->next->txt, 'x');
		}
	}

	if (!computed_jumps) {
		do_stuff(t->start[0]);
		for (i = 0; i < seen.length; ++i) {
			do_stuff(seen.ops[i]);
		}
	} else {
		for (i = 0; i < t->length; ++i) {
			struct op *op = t->start[i];
			const String *tmp;

			if ((tmp = ve_label(&Venus, op))) {
				if (St_ptr(&op->txt)) {
					fprintf(io_fp(Out), "    FOR %s\n", St_ptr(&op->txt));
				}
				fprintf(io_fp(Out), "%s%s", St_ptr(tmp), &" "[!St_len(tmp)]);
			} else {
				if (St_ptr(&op->txt)) {
					fprintf(io_fp(Out), "FOR %s ", St_ptr(&op->txt));
				}
			}

			dump_op(op);
			io_write_m(Out, "\n", 1);

			if (op->next && op->next != t->start[i + 1] && op->type != OP_IF) {
				fprintf(io_fp(Out), "NEXT %s\n", St_ptr(&op->next->txt));
			}
		}
	}
	xfree(seen.ops);
}