view src/ploki/transmogrify.c @ 12518:2d8fe55c6e65 draft default tip

<int-e> learn The password of the month is release incident pilot.
author HackEso <hackeso@esolangs.org>
date Sun, 03 Nov 2024 00:31:02 +0000
parents ac0403686959
children
line wrap: on
line source

#include "config.h"
#include "Str.h"
#include "expr.h"
#include "kork.h"
#include "main_label.h"
#include "op.h"
#include "text.h"
#include "transmogrify.h"
#include "val.h"
#include "venus.h"
#include "xmalloc.h"
#include "zz.h"

#include <ctype.h>
#include <math.h>
#include <stddef.h>
#include <assert.h>

ATTR_PURE
static int liter_expr(const struct expr *const e) {
	return
		e->type == literE ||
		(
		 e->type == symbolE &&
		 e->op != S_ARG && e->op != S_ARGC  && e->op != S_ARGV &&
		 e->op != S_ERR && e->op != S_MATCH && e->op != S_RAND &&
		 e->op != S_RESULT
		) ||
		(
		 e->type == listE &&
		 (e->right == NULL || liter_expr(e->right)) &&
		 e->left.expr == NULL
		)
		;
}

#define const_unop(op) \
( \
 (op) == F_ABS   || (op) == F_ACOS   || (op) == F_ASIN    || \
 (op) == F_ATAN  || (op) == F_ATAN2  || (op) == F_CHR     || \
 (op) == F_CHR   || (op) == F_COS    || (op) == F_DEFINED || \
 (op) == F_EXP   || (op) == F_GETENV || (op) == F_INT     || \
 (op) == F_IO    || (op) == F_LENGTH || (op) == F_LOG     || \
 (op) == F_LOG10 || (op) == F_LOWER  || (op) == F_NEG     || \
 (op) == F_NOT   || (op) == F_NUM    || (op) == F_ORD     || \
 (op) == F_QUOTE || (op) == F_RE_ESC || (op) == F_REVERSE || \
 (op) == F_SIN   || (op) == F_TAN    || (op) == F_SQRT    || \
 (op) == F_STR   || (op) == F_TYPEOF || (op) == F_UPPER      \
)

#define const_binop(op)                                      \
(                                                            \
  !((op) == B_SPOT || (op) == B_SQIGGLE || (op) == B_XMATCH) \
)

static int hp_expr(const struct expr *e) {
	switch (e->type) {
		case literE:
			return 1;

		case varE:
			return 1;

		case varhashE:
			return hp_expr(e->right);

		case symbolE:
			if (e->op != S_RAND) {
				return 1;
			}
			return 0;

		case unopE:
			if (!const_unop(e->op)) {
				return 0;
			}
			return hp_expr(e->right);

		case binopE:
			if (!const_binop(e->op)) {
				return 0;
			}
			if (!hp_expr(e->left.expr)) {
				return 0;
			}
			return hp_expr(e->right);

		case listE:
			if (e->right) {
				if (!hp_expr(e->right)) {
					return 0;
				}
				if (!e->left.expr) {
					return 1;
				}
				return hp_expr(e->left.expr);
			}
			return 1;
	}

	NOTREACHED;
}

void trans_fold(struct expr **e) {
	struct val *v;

	switch ((*e)->type) {
		case literE:
			break;

		case varE:
			break;

		case varhashE:
			trans_fold(&(*e)->right);
			break;

		case symbolE:
			break;

		case unopE:
			trans_fold(&(*e)->right);
			if (liter_expr((*e)->right)) {
				if ((*e)->op == F_NUL) {
					v = eval_expr((*e)->right);
					free_expr((*e)->right);
					(*e)->right = xmalloc(1, sizeof *(*e)->right);
					(*e)->right->type = literE;
					(*e)->right->left.expr = (*e)->right->right = NULL;
					(*e)->right->v.val = v_undef();
					TOLABEL(v);
					if (((*e)->left.op = ve_findnext(&Venus, ko_str(v->ko), (*e)->left.bonus))) {
						(*e)->op = F_CALL;
					} else {
						(*e)->op = F_HANG;
					}
					v_delete(v);
				} else if (const_unop((*e)->op)) {
					(*e)->v.val = eval_expr(*e);
					free_expr((*e)->right);
					(*e)->right = NULL;
					(*e)->type = literE;
				}
			} else if (
				(*e)->right->type == unopE &&
				(*e)->op == (*e)->right->op &&
				((*e)->op == F_STR || (*e)->op == F_NUM)
			) {
				struct expr *const tmp = (*e)->right;
				(*e)->right = (*e)->right->right;
				tmp->right = NULL;
				free_expr(tmp);
			}
			break;

		case binopE:
			trans_fold(&(*e)->left.expr);
			trans_fold(&(*e)->right);
			if (liter_expr((*e)->left.expr) && liter_expr((*e)->right) && const_binop((*e)->op)) {
				(*e)->v.val = eval_expr(*e);
				free_expr((*e)->left.expr);
				(*e)->left.expr = NULL;
				free_expr((*e)->right);
				(*e)->right = NULL;
				(*e)->type = literE;
			} else switch ((*e)->op) {
				case B_INTERSECTION:
					if ((*e)->left.expr->type == literE) {
						V_NUM((*e)->left.expr->v.val);
						if ((*e)->left.expr->v.val->num == 0.0) {
							free_expr((*e)->left.expr);
							(*e)->left.expr = NULL;
							(*e)->op = F_NUM;
							(*e)->type = unopE;
						}
					} else if ((*e)->right->type == literE) {
						V_NUM((*e)->right->v.val);
						if ((*e)->right->v.val->num == 0.0) {
							free_expr((*e)->right);
							(*e)->right = (*e)->left.expr;
							(*e)->left.expr = NULL;
							(*e)->op = F_NUM;
							(*e)->type = unopE;
						}
					}
					break;

				case B_TAIL:
					if (hp_expr((*e)->left.expr)) {
						struct expr *tmp;

						free_expr((*e)->left.expr);
						tmp = *e;
						*e = (*e)->right;
						xfree(tmp);
					}
					break;

				case B_WORM:
					if ((*e)->left.expr->type == literE) {
						V_NUM((*e)->left.expr->v.val);
						if ((*e)->left.expr->v.val->num == 0.0) {
							free_expr((*e)->left.expr);
							(*e)->left.expr = NULL;
							(*e)->op = F_NEG;
							(*e)->type = unopE;
						}
					} else if ((*e)->right->type == literE) {
						V_NUM((*e)->right->v.val);
						if ((*e)->right->v.val->num == 0.0) {
							free_expr((*e)->right);
							(*e)->right = (*e)->left.expr;
							(*e)->left.expr = NULL;
							(*e)->op = F_NUM;
							(*e)->type = unopE;
						}
					}
					break;

				case B_SHARK_FIN:
					if ((*e)->left.expr->type == symbolE) {
						if ((*e)->left.expr->op == S_EULER) {
							free_expr((*e)->left.expr);
							(*e)->left.expr = NULL;
							(*e)->op = F_EXP;
							(*e)->type = unopE;
						}
					} else if ((*e)->right->type == literE) {
						V_NUM((*e)->right->v.val);
						if ((*e)->right->v.val->num == 0.5) {
							free_expr((*e)->right);
							(*e)->right = (*e)->left.expr;
							(*e)->left.expr = NULL;
							(*e)->op = F_SQRT;
							(*e)->type = unopE;
						}
					}
					break;

				case B_FLATWORM:
					if ((*e)->left.expr->type == literE) {
						V_STR((*e)->left.expr->v.val);
						if (!ko_length((*e)->left.expr->v.val->ko)) {
							free_expr((*e)->left.expr);
							(*e)->left.expr = NULL;
							(*e)->op = F_STR;
							(*e)->type = unopE;
						}
					} else if ((*e)->right->type == literE) {
						V_STR((*e)->right->v.val);
						if (!ko_length((*e)->right->v.val->ko)) {
							free_expr((*e)->right);
							(*e)->right = (*e)->left.expr;
							(*e)->left.expr = NULL;
							(*e)->op = F_STR;
							(*e)->type = unopE;
						}
					}
					break;

				case B_SQIGGLE:
				case B_XMATCH:
					if ((*e)->right->type == literE) {
						t_regex *rx;
						V_STR((*e)->right->v.val);
						rx = re_compile(ko_str((*e)->right->v.val->ko));
						free_expr((*e)->right);
						(*e)->right = (*e)->left.expr;
						(*e)->left.rx = rx;
						(*e)->op = F_MATCH;
						(*e)->type = unopE;
					}
					break;
			}
			break;

		case listE:
			if ((*e)->right) {
				trans_fold(&(*e)->right);
				if ((*e)->left.expr) {
					trans_fold(&(*e)->left.expr);
				}
			}
			break;
	}
}

#define SNAP(p)                    \
do {                               \
	while (                        \
			(p) &&                 \
			(p)->type == OP_NOP && \
			(p) != (p)->next       \
		  ) {                      \
		(p) = (p)->next;           \
	}                              \
} while (0)

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

			case varE:
				return;

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

			case unopE:
				if (e->op == F_CALL) {
					SNAP(e->left.op);
					if (
							e->left.op &&
							e->left.op->type == OP_RETURN &&
							hp_expr(e->right) &&
							liter_expr(e->left.op->arg)
					   ) {
						free_expr(e->right);
						e->right = NULL;
						switch (e->left.op->arg->type) {
							case literE:
								e->v.val = v_undef();
								v_set(e->v.val, e->left.op->arg->v.val);
								e->type = literE;
								break;

							case symbolE:
								e->op = e->left.op->arg->op;
								e->type = symbolE;
								break;

							case listE:
								assert(!e->left.op->arg->left.expr && !e->left.op->arg->right);
								e->type = listE;
								break;

							default:
								NOTREACHED;
						}
						e->left.expr = NULL;
						return;
					}
				}
				e = e->right;
				continue;

			case binopE:
				walk(e->left.expr);
				e = e->right;
				continue;

			case listE:
				if (e->right) {
					if (e->left.expr) {
						walk(e->left.expr);
					}
					e = e->right;
					continue;
				}
				return;
		}
	}
}

void transmogrify(struct text *code) {
	size_t i;
	struct op *hang = NULL;

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

		switch (op->type) {
			case OP_CALL:
			case OP_CALL_DYN:
			case OP_CLOSE:
			case OP_EXIT:
			case OP_FLUSH:
			case OP_GOBACK:
			case OP_GOTO:
			case OP_IF:
			case OP_RESET:
			case OP_RETURN:
			case OP_SYSTEM:
			case OP_THROW:
				trans_fold(&op->arg);
				break;

			case OP_ASSIGN:
				trans_fold(&op->arg);
				if (!op->arh.expr) {
					if (hp_expr(op->arg)) {
						free_expr(op->arg);
						op->arg = NULL;
						op->type = OP_NOP;
					}
				} else {
					trans_fold(&op->arh.expr);
					if (
							op->arh.expr->type == varE &&
							op->arg->type == binopE &&
							op->arg->left.expr->type == varE &&
							op->arh.expr->v.val == op->arg->left.expr->v.val
					   ) {
						struct expr *const tmp = op->arg;
						op->arh.expr->op = op->arg->op;
						op->arg = op->arg->right;
						xfree(tmp->left.expr);
						xfree(tmp);
						op->type = OP_MODIFY;
					} else if (op->arg->type == literE) {
						op->type = OP_SET_VAL;
					}
				}
				break;

			case OP_CALL_BACK:
			case OP_MODIFY:
				trans_fold(&op->arh.expr);
				trans_fold(&op->arg);
				break;

			case OP_PRINT:
			case OP_PUTC:
			case OP_TEMP:
				trans_fold(&op->arg);
				if (op->arh.expr) {
					trans_fold(&op->arh.expr);
				}
				break;

			default:
				break;
		}
	}

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

		switch (op->type) {
			case OP_CALL_BACK:
				if (liter_expr(op->arh.expr)) {
					struct val *v;

					v = eval_expr(op->arh.expr);
					TOLABEL(v);
					free_expr(op->arh.expr);
					if ((op->arh.op = ve_findprev(&Venus, ko_str(v->ko), op->line))) {
						op->type = OP_CALL;
					} else {
						free_expr(op->arg);
						op->arg = NULL;
						op->type = OP_HANG;
						hang = op;
					}
					v_end(v);
					xfree(v);
				}
				break;

			case OP_CALL_DYN:
			case OP_GOBACK:
			case OP_GOTO:
				if (liter_expr(op->arg)) {
					struct val *v;

					v = eval_expr(op->arg);
					TOLABEL(v);
					free_expr(op->arg);
					op->arg = NULL;
					if ((op->arh.op = (op->type == OP_GOBACK ? ve_findprev : ve_findnext)(&Venus, ko_str(v->ko), op->line))) {
						if (op->type == OP_CALL_DYN) {
							op->type = OP_CALL;
							op->arg = xmalloc(1, sizeof *op->arg);
							op->arg->left.expr = op->arg->right = NULL;
							op->arg->type = literE;
							op->arg->v.val = v_undef();
						} else {
							op->next = op->arh.op;
							op->arh.op = NULL;
							op->type = OP_NOP;
						}
					} else {
						op->type = OP_HANG;
						hang = op;
					}
					v_end(v);
					xfree(v);
				}
				break;

			case OP_HANG:
				hang = op;
				break;

			case OP_IF:
				if (liter_expr(op->arg)) {
					struct val *v;

					v = eval_expr(op->arg);
					if (v_true(v)) {
						op->next = op->arh.op;
					}
					v_delete(v);
					op->type = OP_NOP;
					op->arh.expr = NULL;
					free_expr(op->arg);
					op->arg = NULL;
				}
				break;

			case OP_PRINT:
				if (
						op->arg->type == literE &&
						(
						 (
						  op->arg->v.val->type == V_STR_K &&
						  ko_length(op->arg->v.val->ko) == 0
						 ) ||
						 op->arg->v.val->type == V_UNDEF
						)
				   ) {
					op->type = OP_NOP;
					free_expr(op->arg);
					op->arg = NULL;
					if (op->arh.expr) {
						free_expr(op->arh.expr);
						op->arh.expr = NULL;
					}
					break;
				}
				if (
						op->arh.expr &&
						op->arh.expr->type == symbolE &&
						op->arh.expr->op == S_STDOUT
				   ) {
					free_expr(op->arh.expr);
					op->arh.expr = NULL;
				}
				break;

			default:
				break;
		}
	}

	if (!hang) {
		struct op tmp;

		tmp.type = OP_HANG;
		tmp.arg = tmp.arh.expr = NULL;
		tmp.next = NULL;
		St_init(&tmp.txt);
		St_clear(&tmp.txt);
		tmp.line = -1;

		hang = text_push(code, &tmp);
	}

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

		SNAP(op->next);
		if (op->type == OP_IF) {
			SNAP(op->arh.op);
		} else if (op->type == OP_CALL) {
			SNAP(op->arh.op);
			if (!op->arh.op) {
				struct expr *tmp = xmalloc(1, sizeof *tmp);
				tmp->type = binopE;
				tmp->op = B_TAIL;
				tmp->left.expr = op->arg;
				tmp->right = xmalloc(1, sizeof *tmp->right);
				tmp->right->type = literE;
				tmp->right->v.val = v_undef();
				tmp->right->left.expr = tmp->right->right = NULL;
				op->arg = tmp;
				op->type = OP_EXIT;
				trans_fold(&op->arg);
			}
		}

		if (op == op->next) {
			op->next = hang;
		}
		if (OP_1ARG_P(op->type)) {
			walk(op->arg);
		} else if (OP_2ARG_P(op->type)) {
			if (op->arh.expr) {
				walk(op->arh.expr);
			}
			walk(op->arg);
		}
	}
}