view src/ploki/transmogrify.c @ 8065:591b1467ccdf

<int-e> le/rn paste/"Paste" is a short story by Henry James. Its contents has been cut into pieces and distributed over numerous tin boxes on the World Wide Web, little pearls of wisdom buried among ordinary pastes.
author HackBot
date Sun, 15 May 2016 13:14:57 +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);
		}
	}
}